mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-08 21:14:41 -04:00
Merge branch '1.16' into dynregman
This commit is contained in:
commit
bf575bf013
124 changed files with 1914 additions and 715 deletions
build.gradle
fabric-biome-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/biome/v1
impl/biome
BiomeVariant.javaContinentalBiomeEntry.javaInternalBiomeData.javaInternalBiomeUtils.javaVariantTransformer.javaWeightedBiomePicker.java
mixin/biome
resources
testmod
fabric-biomes-v1
fabric-events-interaction-v0
build.gradle
src/main/java/net/fabricmc/fabric/api
fabric-game-rule-api-v1
fabric-item-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/item/v1
impl/item
mixin/item
resources
testmod
java/net/fabricmc/fabric/test/item
resources
fabric-models-v0
fabric-object-builder-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/object/builder/v1
impl/object/builder
mixin/object/builder
resources
testmod
java/net/fabricmc/fabric/test/object/builder
CriterionRegistryTest.javaObjectBuilderTestConstants.javaSimpleTradeFactory.javaVillagerTypeTest1.javaVillagerTypeTest2.java
resources
assets/fabric-object-builder-api-v1-testmod
data/fabric-object-builder-api-v1-testmod/advancements
fabric.mod.jsonfabric-registry-sync-v0
build.gradle
src
main
java/net/fabricmc/fabric
api/event/registry
impl/registry/sync
mixin/registry/sync
resources
testmod/java/net/fabricmc/fabric/test/registry/sync
fabric-renderer-api-v1
build.gradle
src/main/java/net/fabricmc/fabric/api/renderer/v1
fabric-renderer-indigo
|
@ -18,7 +18,7 @@ plugins {
|
|||
def ENV = System.getenv()
|
||||
|
||||
class Globals {
|
||||
static def baseVersion = "0.20.2"
|
||||
static def baseVersion = "0.22.1"
|
||||
static def mcVersion = "1.16.3"
|
||||
static def yarnVersion = "+build.1"
|
||||
}
|
||||
|
|
2
fabric-biome-api-v1/build.gradle
Normal file
2
fabric-biome-api-v1/build.gradle
Normal file
|
@ -0,0 +1,2 @@
|
|||
archivesBaseName = "fabric-biome-api-v1"
|
||||
version = getSubprojectVersion(project, "2.0.0")
|
|
@ -14,26 +14,33 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.biomes.v1;
|
||||
package net.fabricmc.fabric.api.biome.v1;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
|
||||
/**
|
||||
* API that exposes the internals of Minecraft's nether biome code.
|
||||
*
|
||||
* @deprecated Experimental feature, may be removed or changed without further notice.
|
||||
* Because of the volatility of world generation in Minecraft 1.16, this API is marked experimental
|
||||
* since it is likely to change in future Minecraft versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class NetherBiomes {
|
||||
private NetherBiomes() { }
|
||||
private NetherBiomes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a biome to the Nether generator.
|
||||
*
|
||||
* @param biome The biome to add. Must not be null.
|
||||
* @param biome The biome to add. Must not be null.
|
||||
* @param mixedNoisePoint data about the given {@link Biome}'s spawning information in the nether.
|
||||
* @see Biome.MixedNoisePoint
|
||||
*/
|
||||
public static void addNetherBiome(Biome biome, Biome.MixedNoisePoint mixedNoisePoint) {
|
||||
public static void addNetherBiome(RegistryKey<Biome> biome, Biome.MixedNoisePoint mixedNoisePoint) {
|
||||
InternalBiomeData.addNetherBiome(biome, mixedNoisePoint);
|
||||
}
|
||||
}
|
|
@ -14,28 +14,35 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.biomes.v1;
|
||||
package net.fabricmc.fabric.api.biome.v1;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
|
||||
/**
|
||||
* API that exposes some internals of the minecraft default biome source for the overworld.
|
||||
*
|
||||
* @deprecated Experimental feature, may be removed or changed without further notice.
|
||||
* Because of the volatility of world generation in Minecraft 1.16, this API is marked experimental
|
||||
* since it is likely to change in future Minecraft versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class OverworldBiomes {
|
||||
private OverworldBiomes() { }
|
||||
private OverworldBiomes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the biome to the specified climate group, with the specified weight. This is only for the biomes that make up the initial continents in generation.
|
||||
*
|
||||
* @param biome the biome to be added
|
||||
* @param biome the biome to be added
|
||||
* @param climate the climate group whereto the biome is added
|
||||
* @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* @see OverworldClimate for a list of vanilla biome weights
|
||||
*/
|
||||
public static void addContinentalBiome(Biome biome, OverworldClimate climate, double weight) {
|
||||
public static void addContinentalBiome(RegistryKey<Biome> biome, OverworldClimate climate, double weight) {
|
||||
InternalBiomeData.addOverworldContinentalBiome(climate, biome, weight);
|
||||
}
|
||||
|
||||
|
@ -43,12 +50,12 @@ public final class OverworldBiomes {
|
|||
* Adds the biome as a hills variant of the parent biome, with the specified weight.
|
||||
*
|
||||
* @param parent the biome to where the hills variant is added
|
||||
* @param hills the biome to be set as a hills variant
|
||||
* @param hills the biome to be set as a hills variant
|
||||
* @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* Mods should use 1.0 as the default/normal weight.
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* Mods should use 1.0 as the default/normal weight.
|
||||
*/
|
||||
public static void addHillsBiome(Biome parent, Biome hills, double weight) {
|
||||
public static void addHillsBiome(RegistryKey<Biome> parent, RegistryKey<Biome> hills, double weight) {
|
||||
InternalBiomeData.addOverworldHillsBiome(parent, hills, weight);
|
||||
}
|
||||
|
||||
|
@ -56,12 +63,12 @@ public final class OverworldBiomes {
|
|||
* Adds the biome as a shore/beach biome for the parent biome, with the specified weight.
|
||||
*
|
||||
* @param parent the base biome to where the shore biome is added
|
||||
* @param shore the biome to be added as a shore biome
|
||||
* @param shore the biome to be added as a shore biome
|
||||
* @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* Mods should use 1.0 as the default/normal weight.
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* Mods should use 1.0 as the default/normal weight.
|
||||
*/
|
||||
public static void addShoreBiome(Biome parent, Biome shore, double weight) {
|
||||
public static void addShoreBiome(RegistryKey<Biome> parent, RegistryKey<Biome> shore, double weight) {
|
||||
InternalBiomeData.addOverworldShoreBiome(parent, shore, weight);
|
||||
}
|
||||
|
||||
|
@ -69,12 +76,12 @@ public final class OverworldBiomes {
|
|||
* Adds the biome as an an edge biome (excluding as a beach) of the parent biome, with the specified weight.
|
||||
*
|
||||
* @param parent the base biome to where the edge biome is added
|
||||
* @param edge the biome to be added as an edge biome
|
||||
* @param edge the biome to be added as an edge biome
|
||||
* @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* Mods should use 1.0 as the default/normal weight.
|
||||
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
|
||||
* Mods should use 1.0 as the default/normal weight.
|
||||
*/
|
||||
public static void addEdgeBiome(Biome parent, Biome edge, double weight) {
|
||||
public static void addEdgeBiome(RegistryKey<Biome> parent, RegistryKey<Biome> edge, double weight) {
|
||||
InternalBiomeData.addOverworldEdgeBiome(parent, edge, weight);
|
||||
}
|
||||
|
||||
|
@ -89,11 +96,11 @@ public final class OverworldBiomes {
|
|||
* any climate assigned at this point in the generation.
|
||||
*
|
||||
* @param replaced the base biome that is replaced by a variant
|
||||
* @param variant the biome to be added as a variant
|
||||
* @param chance the chance of replacement of the biome into the variant
|
||||
* @param variant the biome to be added as a variant
|
||||
* @param chance the chance of replacement of the biome into the variant
|
||||
* @param climates the climates in which the variants will occur in (none listed = add variant to all climates)
|
||||
*/
|
||||
public static void addBiomeVariant(Biome replaced, Biome variant, double chance, OverworldClimate... climates) {
|
||||
public static void addBiomeVariant(RegistryKey<Biome> replaced, RegistryKey<Biome> variant, double chance, OverworldClimate... climates) {
|
||||
InternalBiomeData.addOverworldBiomeReplacement(replaced, variant, chance, climates);
|
||||
}
|
||||
|
||||
|
@ -102,9 +109,9 @@ public final class OverworldBiomes {
|
|||
* generate in this biome.
|
||||
*
|
||||
* @param parent the base biome in which the river biome is to be set
|
||||
* @param river the river biome for this biome
|
||||
* @param river the river biome for this biome
|
||||
*/
|
||||
public static void setRiverBiome(Biome parent, Biome river) {
|
||||
public static void setRiverBiome(RegistryKey<Biome> parent, RegistryKey<Biome> river) {
|
||||
InternalBiomeData.setOverworldRiverBiome(parent, river);
|
||||
}
|
||||
}
|
|
@ -14,11 +14,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.biomes.v1;
|
||||
package net.fabricmc.fabric.api.biome.v1;
|
||||
|
||||
/**
|
||||
* Represents the climates of biomes on the overworld continents.
|
||||
*
|
||||
* @deprecated Experimental feature, may be removed or changed without further notice.
|
||||
* Because of the volatility of world generation in Minecraft 1.16, this API is marked experimental
|
||||
* since it is likely to change in future Minecraft versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public enum OverworldClimate {
|
||||
/**
|
||||
* Includes Snowy Tundra (with a weight of 3) and Snowy Taiga (with a weight of 1).
|
|
@ -16,20 +16,21 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.biome;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
|
||||
/**
|
||||
* Represents a biome variant and its corresponding chance.
|
||||
*/
|
||||
final class BiomeVariant {
|
||||
private final Biome variant;
|
||||
private final RegistryKey<Biome> variant;
|
||||
private final double chance;
|
||||
|
||||
/**
|
||||
* @param variant the variant biome
|
||||
* @param chance the chance of replacement of the biome into the variant
|
||||
*/
|
||||
BiomeVariant(final Biome variant, final double chance) {
|
||||
BiomeVariant(final RegistryKey<Biome> variant, final double chance) {
|
||||
this.variant = variant;
|
||||
this.chance = chance;
|
||||
}
|
||||
|
@ -37,7 +38,7 @@ final class BiomeVariant {
|
|||
/**
|
||||
* @return the variant biome
|
||||
*/
|
||||
Biome getVariant() {
|
||||
RegistryKey<Biome> getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
|
@ -16,13 +16,14 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.biome;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
|
||||
/**
|
||||
* Represents a biome and its corresponding weight.
|
||||
*/
|
||||
final class ContinentalBiomeEntry {
|
||||
private final Biome biome;
|
||||
private final RegistryKey<Biome> biome;
|
||||
private final double weight;
|
||||
private final double upperWeightBound;
|
||||
|
||||
|
@ -31,13 +32,13 @@ final class ContinentalBiomeEntry {
|
|||
* @param weight how often a biome will be chosen
|
||||
* @param upperWeightBound the upper weight bound within the context of the other entries, used for the binary search
|
||||
*/
|
||||
ContinentalBiomeEntry(final Biome biome, final double weight, final double upperWeightBound) {
|
||||
ContinentalBiomeEntry(final RegistryKey<Biome> biome, final double weight, final double upperWeightBound) {
|
||||
this.biome = biome;
|
||||
this.weight = weight;
|
||||
this.upperWeightBound = upperWeightBound;
|
||||
}
|
||||
|
||||
Biome getBiome() {
|
||||
RegistryKey<Biome> getBiome() {
|
||||
return biome;
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.biome;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.biome.layer.BiomeLayers;
|
||||
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
|
||||
import net.minecraft.world.gen.feature.StructureFeature;
|
||||
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
|
||||
import net.fabricmc.fabric.mixin.biome.VanillaLayeredBiomeSourceAccessor;
|
||||
|
||||
/**
|
||||
* Lists and maps for internal use only! Stores data that is used by the various mixins into the world generation
|
||||
*/
|
||||
public final class InternalBiomeData {
|
||||
private InternalBiomeData() {
|
||||
}
|
||||
|
||||
private static final EnumMap<OverworldClimate, WeightedBiomePicker> OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS = new EnumMap<>(OverworldClimate.class);
|
||||
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new HashMap<>();
|
||||
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new HashMap<>();
|
||||
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new HashMap<>();
|
||||
private static final Map<RegistryKey<Biome>, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new HashMap<>();
|
||||
private static final Map<RegistryKey<Biome>, RegistryKey<Biome>> OVERWORLD_RIVER_MAP = new HashMap<>();
|
||||
|
||||
private static final Set<RegistryKey<Biome>> NETHER_BIOMES = new HashSet<>();
|
||||
private static final Map<RegistryKey<Biome>, Biome.MixedNoisePoint> NETHER_BIOME_NOISE_POINTS = new HashMap<>();
|
||||
|
||||
public static void addOverworldContinentalBiome(OverworldClimate climate, RegistryKey<Biome> biome, double weight) {
|
||||
Preconditions.checkArgument(climate != null, "Climate is null");
|
||||
Preconditions.checkArgument(biome != null, "Biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
InternalBiomeUtils.ensureIdMapping(biome);
|
||||
OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS.computeIfAbsent(climate, k -> new WeightedBiomePicker()).addBiome(biome, weight);
|
||||
injectOverworldBiome(biome);
|
||||
}
|
||||
|
||||
public static void addOverworldHillsBiome(RegistryKey<Biome> primary, RegistryKey<Biome> hills, double weight) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
Preconditions.checkArgument(hills != null, "Hills biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
InternalBiomeUtils.ensureIdMapping(primary);
|
||||
InternalBiomeUtils.ensureIdMapping(hills);
|
||||
OVERWORLD_HILLS_MAP.computeIfAbsent(primary, biome -> DefaultHillsData.injectDefaultHills(primary, new WeightedBiomePicker())).addBiome(hills, weight);
|
||||
injectOverworldBiome(hills);
|
||||
}
|
||||
|
||||
public static void addOverworldShoreBiome(RegistryKey<Biome> primary, RegistryKey<Biome> shore, double weight) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
Preconditions.checkArgument(shore != null, "Shore biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
InternalBiomeUtils.ensureIdMapping(primary);
|
||||
InternalBiomeUtils.ensureIdMapping(shore);
|
||||
OVERWORLD_SHORE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(shore, weight);
|
||||
injectOverworldBiome(shore);
|
||||
}
|
||||
|
||||
public static void addOverworldEdgeBiome(RegistryKey<Biome> primary, RegistryKey<Biome> edge, double weight) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
Preconditions.checkArgument(edge != null, "Edge biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
InternalBiomeUtils.ensureIdMapping(primary);
|
||||
InternalBiomeUtils.ensureIdMapping(edge);
|
||||
OVERWORLD_EDGE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(edge, weight);
|
||||
injectOverworldBiome(edge);
|
||||
}
|
||||
|
||||
public static void addOverworldBiomeReplacement(RegistryKey<Biome> replaced, RegistryKey<Biome> variant, double chance, OverworldClimate[] climates) {
|
||||
Preconditions.checkArgument(replaced != null, "Replaced biome is null");
|
||||
Preconditions.checkArgument(variant != null, "Variant biome is null");
|
||||
Preconditions.checkArgument(chance > 0 && chance <= 1, "Chance is not greater than 0 or less than or equal to 1");
|
||||
InternalBiomeUtils.ensureIdMapping(replaced);
|
||||
InternalBiomeUtils.ensureIdMapping(variant);
|
||||
OVERWORLD_VARIANT_TRANSFORMERS.computeIfAbsent(replaced, biome -> new VariantTransformer()).addBiome(variant, chance, climates);
|
||||
injectOverworldBiome(variant);
|
||||
}
|
||||
|
||||
public static void setOverworldRiverBiome(RegistryKey<Biome> primary, RegistryKey<Biome> river) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
InternalBiomeUtils.ensureIdMapping(primary);
|
||||
InternalBiomeUtils.ensureIdMapping(river);
|
||||
OVERWORLD_RIVER_MAP.put(primary, river);
|
||||
|
||||
if (river != null) {
|
||||
injectOverworldBiome(river);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the biomes in world gen to the array for the vanilla layered biome source.
|
||||
* This helps with {@link VanillaLayeredBiomeSource#hasStructureFeature(StructureFeature)} returning correctly for modded biomes as well as in {@link VanillaLayeredBiomeSource#getTopMaterials()}}
|
||||
*/
|
||||
private static void injectOverworldBiome(RegistryKey<Biome> biome) {
|
||||
List<RegistryKey<Biome>> biomes = VanillaLayeredBiomeSourceAccessor.getBIOMES();
|
||||
|
||||
if (biomes instanceof ImmutableList) {
|
||||
biomes = new ArrayList<>(biomes);
|
||||
VanillaLayeredBiomeSourceAccessor.setBIOMES(biomes);
|
||||
}
|
||||
|
||||
biomes.add(biome);
|
||||
}
|
||||
|
||||
public static void addNetherBiome(RegistryKey<Biome> biome, Biome.MixedNoisePoint spawnNoisePoint) {
|
||||
Preconditions.checkArgument(biome != null, "Biome is null");
|
||||
Preconditions.checkArgument(spawnNoisePoint != null, "Biome.MixedNoisePoint is null");
|
||||
InternalBiomeUtils.ensureIdMapping(biome);
|
||||
NETHER_BIOMES.add(biome);
|
||||
NETHER_BIOME_NOISE_POINTS.put(biome, spawnNoisePoint);
|
||||
}
|
||||
|
||||
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getOverworldHills() {
|
||||
return OVERWORLD_HILLS_MAP;
|
||||
}
|
||||
|
||||
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getOverworldShores() {
|
||||
return OVERWORLD_SHORE_MAP;
|
||||
}
|
||||
|
||||
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getOverworldEdges() {
|
||||
return OVERWORLD_EDGE_MAP;
|
||||
}
|
||||
|
||||
public static Map<RegistryKey<Biome>, RegistryKey<Biome>> getOverworldRivers() {
|
||||
return OVERWORLD_RIVER_MAP;
|
||||
}
|
||||
|
||||
public static EnumMap<OverworldClimate, WeightedBiomePicker> getOverworldModdedContinentalBiomePickers() {
|
||||
return OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS;
|
||||
}
|
||||
|
||||
public static Map<RegistryKey<Biome>, VariantTransformer> getOverworldVariantTransformers() {
|
||||
return OVERWORLD_VARIANT_TRANSFORMERS;
|
||||
}
|
||||
|
||||
public static Map<RegistryKey<Biome>, Biome.MixedNoisePoint> getNetherBiomeNoisePoints() {
|
||||
return NETHER_BIOME_NOISE_POINTS;
|
||||
}
|
||||
|
||||
private static class DefaultHillsData {
|
||||
private static final ImmutableMap<RegistryKey<Biome>, RegistryKey<Biome>> DEFAULT_HILLS;
|
||||
|
||||
static WeightedBiomePicker injectDefaultHills(RegistryKey<Biome> base, WeightedBiomePicker picker) {
|
||||
RegistryKey<Biome> defaultHill = DEFAULT_HILLS.get(base);
|
||||
|
||||
if (defaultHill != null) {
|
||||
picker.addBiome(defaultHill, 1);
|
||||
} else if (BiomeLayers.areSimilar(InternalBiomeUtils.getRawId(base), InternalBiomeUtils.getRawId(BiomeKeys.WOODED_BADLANDS_PLATEAU))) {
|
||||
picker.addBiome(BiomeKeys.BADLANDS, 1);
|
||||
} else if (base == BiomeKeys.DEEP_OCEAN || base == BiomeKeys.DEEP_LUKEWARM_OCEAN || base == BiomeKeys.DEEP_COLD_OCEAN) {
|
||||
picker.addBiome(BiomeKeys.PLAINS, 1);
|
||||
picker.addBiome(BiomeKeys.FOREST, 1);
|
||||
} else if (base == BiomeKeys.DEEP_FROZEN_OCEAN) {
|
||||
// Note: Vanilla Deep Frozen Oceans only have a 1/3 chance of having default hills.
|
||||
// This is a clever trick that ensures that when a mod adds hills with a weight of 1, the 1/3 chance is fulfilled.
|
||||
// 0.5 + 1.0 = 1.5, and 0.5 / 1.5 = 1/3.
|
||||
|
||||
picker.addBiome(BiomeKeys.PLAINS, 0.25);
|
||||
picker.addBiome(BiomeKeys.FOREST, 0.25);
|
||||
} else if (base == BiomeKeys.PLAINS) {
|
||||
picker.addBiome(BiomeKeys.WOODED_HILLS, 1);
|
||||
picker.addBiome(BiomeKeys.FOREST, 2);
|
||||
}
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
static {
|
||||
// This map mirrors the hardcoded logic in AddHillsLayer#sample
|
||||
ImmutableMap.Builder<RegistryKey<Biome>, RegistryKey<Biome>> builder = ImmutableMap.builder();
|
||||
builder.put(BiomeKeys.DESERT, BiomeKeys.DESERT_HILLS);
|
||||
builder.put(BiomeKeys.FOREST, BiomeKeys.WOODED_HILLS);
|
||||
builder.put(BiomeKeys.BIRCH_FOREST, BiomeKeys.BIRCH_FOREST_HILLS);
|
||||
builder.put(BiomeKeys.DARK_FOREST, BiomeKeys.PLAINS);
|
||||
builder.put(BiomeKeys.TAIGA, BiomeKeys.TAIGA_HILLS);
|
||||
builder.put(BiomeKeys.GIANT_TREE_TAIGA, BiomeKeys.GIANT_TREE_TAIGA_HILLS);
|
||||
builder.put(BiomeKeys.SNOWY_TAIGA, BiomeKeys.SNOWY_TAIGA_HILLS);
|
||||
builder.put(BiomeKeys.SNOWY_TUNDRA, BiomeKeys.SNOWY_MOUNTAINS);
|
||||
builder.put(BiomeKeys.JUNGLE, BiomeKeys.JUNGLE_HILLS);
|
||||
builder.put(BiomeKeys.BAMBOO_JUNGLE, BiomeKeys.BAMBOO_JUNGLE_HILLS);
|
||||
builder.put(BiomeKeys.OCEAN, BiomeKeys.DEEP_OCEAN);
|
||||
builder.put(BiomeKeys.LUKEWARM_OCEAN, BiomeKeys.DEEP_LUKEWARM_OCEAN);
|
||||
builder.put(BiomeKeys.COLD_OCEAN, BiomeKeys.DEEP_COLD_OCEAN);
|
||||
builder.put(BiomeKeys.FROZEN_OCEAN, BiomeKeys.DEEP_FROZEN_OCEAN);
|
||||
builder.put(BiomeKeys.MOUNTAINS, BiomeKeys.WOODED_MOUNTAINS);
|
||||
builder.put(BiomeKeys.SAVANNA, BiomeKeys.SAVANNA_PLATEAU);
|
||||
DEFAULT_HILLS = builder.build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,28 +20,35 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
import net.minecraft.world.dimension.DimensionOptions;
|
||||
import net.minecraft.world.gen.GeneratorOptions;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
|
||||
import net.fabricmc.fabric.mixin.biome.DimensionOptionsAccessor;
|
||||
import net.fabricmc.fabric.mixin.biome.DimensionTypeAccessor;
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BuiltinBiomes;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
|
||||
import net.fabricmc.fabric.mixin.biome.AddHillsLayerAccessor;
|
||||
import net.fabricmc.fabric.mixin.biome.BuiltinBiomesAccessor;
|
||||
|
||||
/**
|
||||
* Internal utilities used for biome sampling.
|
||||
*/
|
||||
public final class InternalBiomeUtils {
|
||||
private InternalBiomeUtils() { }
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
private InternalBiomeUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param north raw id of the biome to the north
|
||||
* @param east raw id of the biome to the east
|
||||
* @param south raw id of the biome to the south
|
||||
* @param west raw id of the biome to the west
|
||||
* @param north raw id of the biome to the north
|
||||
* @param east raw id of the biome to the east
|
||||
* @param south raw id of the biome to the south
|
||||
* @param west raw id of the biome to the west
|
||||
* @param center central biome that comparisons are relative to
|
||||
* @return whether the central biome is an edge of a biome
|
||||
*/
|
||||
|
@ -50,7 +57,7 @@ public final class InternalBiomeUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param mainBiomeId the main raw biome id in comparison
|
||||
* @param mainBiomeId the main raw biome id in comparison
|
||||
* @param secondaryBiomeId the secondary raw biome id in comparison
|
||||
* @return whether the two biomes are unsimilar
|
||||
*/
|
||||
|
@ -58,21 +65,23 @@ public final class InternalBiomeUtils {
|
|||
if (mainBiomeId == secondaryBiomeId) { // for efficiency, determine if the ids are equal first
|
||||
return false;
|
||||
} else {
|
||||
Biome secondaryBiome = BuiltinRegistries.BIOME.get(secondaryBiomeId);
|
||||
Biome mainBiome = BuiltinRegistries.BIOME.get(mainBiomeId);
|
||||
// Regard a biome as "similar" to it's derived biome, i.e.
|
||||
// No edge between plains and sunflower plains
|
||||
|
||||
boolean isUnsimilar = secondaryBiome.hasParent() ? !(mainBiomeId == BuiltinRegistries.BIOME.getRawId(BuiltinRegistries.BIOME.get(new Identifier(secondaryBiome.getParent())))) : true;
|
||||
isUnsimilar = isUnsimilar && (mainBiome.hasParent() ? !(secondaryBiomeId == BuiltinRegistries.BIOME.getRawId(BuiltinRegistries.BIOME.get(new Identifier(mainBiome.getParent())))) : true);
|
||||
|
||||
return isUnsimilar;
|
||||
// The parent-child relationship previously modeled in Biome itself is gone,
|
||||
// and has been - for the time being - replaced by a hardcoded raw-id map
|
||||
// in AddHillsLayer.
|
||||
Int2IntMap parentChildMap = AddHillsLayerAccessor.getBaseToVariantMap();
|
||||
return parentChildMap.get(mainBiomeId) != secondaryBiomeId
|
||||
&& parentChildMap.get(secondaryBiomeId) != mainBiomeId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param north raw id of the biome to the north
|
||||
* @param east raw id of the biome to the east
|
||||
* @param east raw id of the biome to the east
|
||||
* @param south raw id of the biome to the south
|
||||
* @param west raw id of the biome to the west
|
||||
* @param west raw id of the biome to the west
|
||||
* @return whether a biome in any direction is an ocean around the central biome
|
||||
*/
|
||||
public static boolean neighborsOcean(int north, int east, int south, int west) {
|
||||
|
@ -105,20 +114,21 @@ public final class InternalBiomeUtils {
|
|||
/**
|
||||
* Potentially transforms a biome into its variants based on the provided randomness source.
|
||||
*
|
||||
* @param random The randomness source
|
||||
* @param random The randomness source
|
||||
* @param existing The base biome
|
||||
* @param climate The climate in which the biome resides, or null to indicate an unknown climate
|
||||
* @param climate The climate in which the biome resides, or null to indicate an unknown climate
|
||||
* @return The potentially transformed biome
|
||||
*/
|
||||
public static int transformBiome(LayerRandomnessSource random, Biome existing, OverworldClimate climate) {
|
||||
Map<Biome, VariantTransformer> overworldVariantTransformers = InternalBiomeData.getOverworldVariantTransformers();
|
||||
public static int transformBiome(LayerRandomnessSource random, RegistryKey<Biome> existing, OverworldClimate climate) {
|
||||
Map<RegistryKey<Biome>, VariantTransformer> overworldVariantTransformers = InternalBiomeData.getOverworldVariantTransformers();
|
||||
VariantTransformer transformer = overworldVariantTransformers.get(existing);
|
||||
|
||||
if (transformer != null) {
|
||||
return BuiltinRegistries.BIOME.getRawId(transformer.transformBiome(existing, random, climate));
|
||||
RegistryKey<Biome> key = transformer.transformBiome(existing, random, climate);
|
||||
return getRawId(key);
|
||||
}
|
||||
|
||||
return BuiltinRegistries.BIOME.getRawId(existing);
|
||||
return getRawId(existing);
|
||||
}
|
||||
|
||||
public static void injectBiomesIntoClimate(LayerRandomnessSource random, int[] vanillaArray, OverworldClimate climate, IntConsumer result) {
|
||||
|
@ -138,7 +148,7 @@ public final class InternalBiomeUtils {
|
|||
if (reqWeightSum < vanillaArray.length) {
|
||||
// Vanilla biome; look it up from the vanilla array and transform accordingly.
|
||||
|
||||
result.accept(transformBiome(random, BuiltinRegistries.BIOME.get(vanillaArray[(int) reqWeightSum]), climate));
|
||||
result.accept(transformBiome(random, BuiltinBiomes.fromRawId(vanillaArray[(int) reqWeightSum]), climate));
|
||||
} else {
|
||||
// Modded biome; use a binary search, and then transform accordingly.
|
||||
|
||||
|
@ -148,7 +158,22 @@ public final class InternalBiomeUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void recreateChunkGenerators(GeneratorOptions generatorOptions) {
|
||||
((DimensionOptionsAccessor) (Object) generatorOptions.getDimensionMap().get(DimensionOptions.NETHER)).setChunkGenerator(DimensionTypeAccessor.createNetherGenerator(generatorOptions.getSeed()));
|
||||
public static int getRawId(RegistryKey<Biome> key) {
|
||||
return BuiltinRegistries.BIOME.getRawId(BuiltinRegistries.BIOME.getOrThrow(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the given registry key is mapped in {@link BuiltinBiomes}. This mapping may be absent
|
||||
* if mods register their biomes only in {@link BuiltinRegistries#BIOME}, and not using the
|
||||
* private method in {@link BuiltinBiomes}.
|
||||
*/
|
||||
public static void ensureIdMapping(RegistryKey<Biome> biomeKey) {
|
||||
int rawId = getRawId(biomeKey);
|
||||
Int2ObjectMap<RegistryKey<Biome>> biomes = BuiltinBiomesAccessor.getBY_RAW_ID();
|
||||
|
||||
if (!biomes.containsKey(rawId)) {
|
||||
LOGGER.debug("Automatically creating layer-related raw-id mapping for biome {}", biomeKey);
|
||||
biomes.put(rawId, biomeKey);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,10 +21,11 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
|
||||
|
||||
/**
|
||||
* Deals with picking variants for you.
|
||||
|
@ -38,7 +39,7 @@ final class VariantTransformer {
|
|||
* @param chance the chance of replacement of the biome into the variant
|
||||
* @param climates the climates that the variant can replace the base biome in, empty/null indicates all climates
|
||||
*/
|
||||
void addBiome(Biome variant, double chance, OverworldClimate[] climates) {
|
||||
void addBiome(RegistryKey<Biome> variant, double chance, OverworldClimate[] climates) {
|
||||
if (climates == null || climates.length == 0) {
|
||||
defaultTransformer.addBiome(variant, chance);
|
||||
climates = OverworldClimate.values();
|
||||
|
@ -56,7 +57,7 @@ final class VariantTransformer {
|
|||
* @param random the {@link LayerRandomnessSource} from the layer
|
||||
* @return the transformed biome
|
||||
*/
|
||||
Biome transformBiome(Biome replaced, LayerRandomnessSource random, OverworldClimate climate) {
|
||||
RegistryKey<Biome> transformBiome(RegistryKey<Biome> replaced, LayerRandomnessSource random, OverworldClimate climate) {
|
||||
if (climate == null) {
|
||||
return defaultTransformer.transformBiome(replaced, random);
|
||||
}
|
||||
|
@ -77,7 +78,7 @@ final class VariantTransformer {
|
|||
* @param variant the variant that the replaced biome is replaced with
|
||||
* @param chance the chance of replacement of the biome into the variant
|
||||
*/
|
||||
private void addBiome(Biome variant, double chance) {
|
||||
private void addBiome(RegistryKey<Biome> variant, double chance) {
|
||||
variants.add(new BiomeVariant(variant, chance));
|
||||
}
|
||||
|
||||
|
@ -88,7 +89,7 @@ final class VariantTransformer {
|
|||
* @param random the {@link LayerRandomnessSource} from the layer
|
||||
* @return the transformed biome
|
||||
*/
|
||||
private Biome transformBiome(Biome replaced, LayerRandomnessSource random) {
|
||||
private RegistryKey<Biome> transformBiome(RegistryKey<Biome> replaced, LayerRandomnessSource random) {
|
||||
for (BiomeVariant variant : variants) {
|
||||
if (random.nextInt(Integer.MAX_VALUE) < variant.getChance() * Integer.MAX_VALUE) {
|
||||
return variant.getVariant();
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
|
||||
|
@ -36,7 +37,7 @@ public final class WeightedBiomePicker {
|
|||
entries = new ArrayList<>();
|
||||
}
|
||||
|
||||
void addBiome(final Biome biome, final double weight) {
|
||||
void addBiome(final RegistryKey<Biome> biome, final double weight) {
|
||||
currentTotal += weight;
|
||||
|
||||
entries.add(new ContinentalBiomeEntry(biome, weight, currentTotal));
|
||||
|
@ -46,7 +47,7 @@ public final class WeightedBiomePicker {
|
|||
return currentTotal;
|
||||
}
|
||||
|
||||
public Biome pickRandom(LayerRandomnessSource random) {
|
||||
public RegistryKey<Biome> pickRandom(LayerRandomnessSource random) {
|
||||
double target = random.nextInt(Integer.MAX_VALUE) * getCurrentWeightTotal() / Integer.MAX_VALUE;
|
||||
|
||||
return search(target).getBiome();
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.world.biome.layer.AddHillsLayer;
|
||||
|
||||
@Mixin(AddHillsLayer.class)
|
||||
public interface AddHillsLayerAccessor {
|
||||
/**
|
||||
* This field contains a raw-id to raw-id map for establishing parent/child relationships that
|
||||
* model derived biomes.
|
||||
*
|
||||
* <p>For example, it contains a mapping for 1 -> 129 where 1 is the raw id of plains, while 129 is the raw id
|
||||
* of the sunflower plains, which is derived from plains.
|
||||
*/
|
||||
@Accessor("field_26727")
|
||||
static Int2IntMap getBaseToVariantMap() {
|
||||
throw new AssertionError("mixin");
|
||||
}
|
||||
}
|
|
@ -16,14 +16,18 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.world.dimension.DimensionOptions;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BuiltinBiomes;
|
||||
|
||||
@Mixin(DimensionOptions.class)
|
||||
public interface DimensionOptionsAccessor {
|
||||
@Mixin(BuiltinBiomes.class)
|
||||
public interface BuiltinBiomesAccessor {
|
||||
@Accessor
|
||||
void setChunkGenerator(ChunkGenerator generatorOptions);
|
||||
static Int2ObjectMap<RegistryKey<Biome>> getBY_RAW_ID() {
|
||||
throw new AssertionError("mixin");
|
||||
}
|
||||
}
|
|
@ -21,28 +21,29 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BuiltinBiomes;
|
||||
import net.minecraft.world.biome.layer.AddEdgeBiomesLayer;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
|
||||
|
||||
/**
|
||||
* Adds edges and shores specified in {@link OverworldBiomes#addEdgeBiome(Biome, Biome, double)} and {@link OverworldBiomes#addShoreBiome(Biome, Biome, double)} to the edges layer.
|
||||
* Adds edges and shores specified in {@link OverworldBiomes#addEdgeBiome(RegistryKey, RegistryKey, double)} and {@link OverworldBiomes#addShoreBiome(RegistryKey, RegistryKey, double)} to the edges layer.
|
||||
*/
|
||||
@Mixin(AddEdgeBiomesLayer.class)
|
||||
public class MixinAddEdgeBiomesLayer {
|
||||
@Inject(at = @At("HEAD"), method = "sample", cancellable = true)
|
||||
private void sample(LayerRandomnessSource rand, int north, int east, int south, int west, int center, CallbackInfoReturnable<Integer> info) {
|
||||
Biome centerBiome = BuiltinRegistries.BIOME.get(center);
|
||||
RegistryKey<Biome> centerBiome = BuiltinBiomes.fromRawId(center);
|
||||
|
||||
if (InternalBiomeData.getOverworldShores().containsKey(centerBiome) && InternalBiomeUtils.neighborsOcean(north, east, south, west)) {
|
||||
info.setReturnValue(BuiltinRegistries.BIOME.getRawId(InternalBiomeData.getOverworldShores().get(centerBiome).pickRandom(rand)));
|
||||
info.setReturnValue(InternalBiomeUtils.getRawId(InternalBiomeData.getOverworldShores().get(centerBiome).pickRandom(rand)));
|
||||
} else if (InternalBiomeData.getOverworldEdges().containsKey(centerBiome) && InternalBiomeUtils.isEdge(north, east, south, west, center)) {
|
||||
info.setReturnValue(BuiltinRegistries.BIOME.getRawId(InternalBiomeData.getOverworldEdges().get(centerBiome).pickRandom(rand)));
|
||||
info.setReturnValue(InternalBiomeUtils.getRawId(InternalBiomeData.getOverworldEdges().get(centerBiome).pickRandom(rand)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,28 +16,35 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.Biomes;
|
||||
import net.minecraft.world.biome.BuiltinBiomes;
|
||||
import net.minecraft.world.biome.layer.AddHillsLayer;
|
||||
import net.minecraft.world.biome.layer.BiomeLayers;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
import net.minecraft.world.biome.layer.util.LayerSampler;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
|
||||
import net.fabricmc.fabric.impl.biome.WeightedBiomePicker;
|
||||
|
||||
/**
|
||||
* Injects hills biomes specified from {@link OverworldBiomes#addHillsBiome(Biome, Biome, double)}into the default hills layer.
|
||||
* Injects hills biomes specified from {@link OverworldBiomes#addHillsBiome(RegistryKey, RegistryKey, double)} into the default hills layer.
|
||||
*/
|
||||
@Mixin(AddHillsLayer.class)
|
||||
public class MixinAddHillsLayer {
|
||||
// This maps between from a biome to it's "modified variant" biome, which was previously modeled via parent biomes
|
||||
@Shadow
|
||||
private static Int2IntMap field_26727;
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "sample", cancellable = true)
|
||||
private void sample(LayerRandomnessSource rand, LayerSampler biomeSampler, LayerSampler noiseSampler, int chunkX, int chunkZ, CallbackInfoReturnable<Integer> info) {
|
||||
if (InternalBiomeData.getOverworldHills().isEmpty()) {
|
||||
|
@ -48,9 +55,13 @@ public class MixinAddHillsLayer {
|
|||
final int biomeId = biomeSampler.sample(chunkX, chunkZ);
|
||||
int noiseSample = noiseSampler.sample(chunkX, chunkZ);
|
||||
int processedNoiseSample = (noiseSample - 2) % 29;
|
||||
final Biome biome = BuiltinRegistries.BIOME.get(biomeId);
|
||||
RegistryKey<Biome> key = BuiltinBiomes.fromRawId(biomeId);
|
||||
|
||||
WeightedBiomePicker hillPicker = InternalBiomeData.getOverworldHills().get(biome);
|
||||
if (key == null) {
|
||||
throw new IllegalStateException("Biome sampler returned unregistered Biome ID: " + biomeId);
|
||||
}
|
||||
|
||||
WeightedBiomePicker hillPicker = InternalBiomeData.getOverworldHills().get(key);
|
||||
|
||||
if (hillPicker == null) {
|
||||
// No hills for this biome, fall through to vanilla logic.
|
||||
|
@ -59,12 +70,10 @@ public class MixinAddHillsLayer {
|
|||
}
|
||||
|
||||
if (rand.nextInt(3) == 0 || processedNoiseSample == 0) {
|
||||
int biomeReturn = BuiltinRegistries.BIOME.getRawId(hillPicker.pickRandom(rand));
|
||||
Biome parent;
|
||||
int biomeReturn = InternalBiomeUtils.getRawId(hillPicker.pickRandom(rand));
|
||||
|
||||
if (processedNoiseSample == 0 && biomeReturn != biomeId) {
|
||||
parent = Biomes.getMutated(BuiltinRegistries.BIOME.get(biomeReturn));
|
||||
biomeReturn = parent == null ? biomeId : BuiltinRegistries.BIOME.getRawId(parent);
|
||||
biomeReturn = field_26727.getOrDefault(biomeReturn, biomeId);
|
||||
}
|
||||
|
||||
if (biomeReturn != biomeId) {
|
|
@ -18,42 +18,39 @@ package net.fabricmc.fabric.mixin.biome;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.biome.BuiltinBiomes;
|
||||
import net.minecraft.world.biome.layer.AddRiversLayer;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
import net.minecraft.world.biome.layer.util.LayerSampler;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
|
||||
|
||||
/**
|
||||
* Sets river biomes specified with {@link OverworldBiomes#setRiverBiome(Biome, Biome)}.
|
||||
* Sets river biomes specified with {@link OverworldBiomes#setRiverBiome(RegistryKey, RegistryKey)}.
|
||||
*/
|
||||
@Mixin(AddRiversLayer.class)
|
||||
public class MixinAddRiversLayer {
|
||||
@Shadow
|
||||
@Final
|
||||
private static int RIVER_ID;
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "sample", cancellable = true)
|
||||
private void sample(LayerRandomnessSource rand, LayerSampler landSampler, LayerSampler riverSampler, int x, int z, CallbackInfoReturnable<Integer> info) {
|
||||
int landBiomeId = landSampler.sample(x, z);
|
||||
Biome landBiome = BuiltinRegistries.BIOME.get(landBiomeId);
|
||||
RegistryKey<Biome> landBiomeKey = BuiltinBiomes.fromRawId(landBiomeId);
|
||||
|
||||
int riverBiomeId = riverSampler.sample(x, z);
|
||||
Map<Biome, Biome> overworldRivers = InternalBiomeData.getOverworldRivers();
|
||||
Map<RegistryKey<Biome>, RegistryKey<Biome>> overworldRivers = InternalBiomeData.getOverworldRivers();
|
||||
|
||||
if (overworldRivers.containsKey(landBiome) && riverBiomeId == RIVER_ID) {
|
||||
Biome riverBiome = overworldRivers.get(landBiome);
|
||||
info.setReturnValue(riverBiome == null ? landBiomeId : BuiltinRegistries.BIOME.getRawId(riverBiome));
|
||||
if (overworldRivers.containsKey(landBiomeKey) && BuiltinBiomes.fromRawId(riverBiomeId) == BiomeKeys.RIVER) {
|
||||
RegistryKey<Biome> riverBiome = overworldRivers.get(landBiomeKey);
|
||||
info.setReturnValue(riverBiome == null ? landBiomeId : InternalBiomeUtils.getRawId(riverBiome));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,30 +23,32 @@ import java.util.function.Supplier;
|
|||
import com.mojang.datafixers.util.Pair;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArgs;
|
||||
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
|
||||
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.MultiNoiseBiomeSource;
|
||||
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
|
||||
@Mixin(MultiNoiseBiomeSource.class)
|
||||
/**
|
||||
* This Mixin is responsible for adding mod-biomes to the NETHER preset in the MultiNoiseBiomeSource.
|
||||
*/
|
||||
@Mixin(MultiNoiseBiomeSource.Preset.class)
|
||||
public class MixinMultiNoiseBiomeSource {
|
||||
@Inject(method = "method_28467", at = @At("RETURN"))
|
||||
private static void modifyNoisePoints(long l, CallbackInfoReturnable<MultiNoiseBiomeSource> cir) {
|
||||
MultiNoiseBiomeSource returnedSource = cir.getReturnValue();
|
||||
|
||||
// collect existing noise points in non-immutable map
|
||||
List<Pair<Biome.MixedNoisePoint, Supplier<Biome>>> existingPoints = new ArrayList<>(((MultiNoiseBiomeSourceAccessor) returnedSource).getBiomePoints());
|
||||
// NOTE: This is a lambda-function in the NETHER preset field initializer
|
||||
@ModifyArgs(method = "method_31088", at = @At(value = "INVOKE", target = "net/minecraft/world/biome/source/MultiNoiseBiomeSource.<init> (JLjava/util/List;Ljava/util/Optional;Lnet/minecraft/world/biome/source/MultiNoiseBiomeSource$1;)V"))
|
||||
private static void appendNetherBiomes(Args args, MultiNoiseBiomeSource.Preset preset, Registry<Biome> registry, Long seed) {
|
||||
List<Pair<Biome.MixedNoisePoint, Supplier<Biome>>> biomes = new ArrayList<>(args.get(1));
|
||||
|
||||
// add fabric biome noise point data to list && BiomeSource biome list
|
||||
InternalBiomeData.getNetherBiomeNoisePoints().forEach((biome, noisePoint) -> {
|
||||
existingPoints.add(Pair.of(noisePoint, () -> biome));
|
||||
returnedSource.getBiomes().add(biome);
|
||||
InternalBiomeData.getNetherBiomeNoisePoints().forEach((biomeKey, noisePoint) -> {
|
||||
Biome biome = registry.getOrThrow(biomeKey);
|
||||
// NOTE: Even though we have to pass in suppliers, BiomeSource's ctor will resolve them immediately
|
||||
biomes.add(Pair.of(noisePoint, () -> biome));
|
||||
});
|
||||
|
||||
// modify MultiNoiseBiomeSource list with updated data
|
||||
((MultiNoiseBiomeSourceAccessor) returnedSource).setBiomePoints(existingPoints);
|
||||
args.set(1, biomes);
|
||||
}
|
||||
}
|
|
@ -24,12 +24,14 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.biome.BuiltinBiomes;
|
||||
import net.minecraft.world.biome.layer.SetBaseBiomesLayer;
|
||||
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
|
||||
|
||||
/**
|
||||
|
@ -57,22 +59,6 @@ public class MixinSetBaseBiomesLayer {
|
|||
@Mutable
|
||||
private static int[] DRY_BIOMES;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static int WOODED_BADLANDS_PLATEAU_ID;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static int BADLANDS_PLATEAU_ID;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static int JUNGLE_ID;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static int GIANT_TREE_TAIGA_ID;
|
||||
|
||||
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/world/biome/layer/SetBaseBiomesLayer;chosenGroup1:[I"), method = "sample", cancellable = true)
|
||||
private void injectDryBiomes(LayerRandomnessSource random, int value, CallbackInfoReturnable<Integer> info) {
|
||||
InternalBiomeUtils.injectBiomesIntoClimate(random, DRY_BIOMES, OverworldClimate.DRY, info::setReturnValue);
|
||||
|
@ -96,16 +82,16 @@ public class MixinSetBaseBiomesLayer {
|
|||
@Inject(at = @At("RETURN"), method = "sample", cancellable = true)
|
||||
private void transformVariants(LayerRandomnessSource random, int value, CallbackInfoReturnable<Integer> info) {
|
||||
int biomeId = info.getReturnValueI();
|
||||
Biome biome = BuiltinRegistries.BIOME.get(biomeId);
|
||||
RegistryKey<Biome> biome = BuiltinBiomes.fromRawId(biomeId);
|
||||
|
||||
// Determine what special case this is...
|
||||
OverworldClimate climate;
|
||||
|
||||
if (biomeId == BADLANDS_PLATEAU_ID || biomeId == WOODED_BADLANDS_PLATEAU_ID) {
|
||||
if (biome == BiomeKeys.BADLANDS_PLATEAU || biome == BiomeKeys.WOODED_BADLANDS_PLATEAU) {
|
||||
climate = OverworldClimate.DRY;
|
||||
} else if (biomeId == JUNGLE_ID) {
|
||||
} else if (biome == BiomeKeys.JUNGLE) {
|
||||
climate = OverworldClimate.TEMPERATE;
|
||||
} else if (biomeId == GIANT_TREE_TAIGA_ID) {
|
||||
} else if (biome == BiomeKeys.GIANT_TREE_TAIGA) {
|
||||
climate = OverworldClimate.TEMPERATE;
|
||||
} else {
|
||||
climate = null;
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
|
||||
|
||||
/**
|
||||
* This accessor gives us access to the hardcoded list of overworld biomes.
|
||||
*/
|
||||
@Mixin(VanillaLayeredBiomeSource.class)
|
||||
public interface VanillaLayeredBiomeSourceAccessor {
|
||||
@Accessor
|
||||
static List<RegistryKey<Biome>> getBIOMES() {
|
||||
throw new AssertionError("mixin");
|
||||
}
|
||||
|
||||
@Accessor
|
||||
static void setBIOMES(List<RegistryKey<Biome>> biomes) {
|
||||
throw new AssertionError("mixin");
|
||||
}
|
||||
}
|
Before ![]() (image error) Size: 1.5 KiB After ![]() (image error) Size: 1.5 KiB ![]() ![]() |
|
@ -3,17 +3,15 @@
|
|||
"package": "net.fabricmc.fabric.mixin.biome",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"DimensionOptionsAccessor",
|
||||
"DimensionTypeAccessor",
|
||||
"AddHillsLayerAccessor",
|
||||
"BuiltinBiomesAccessor",
|
||||
"MixinAddEdgeBiomesLayer",
|
||||
"MixinAddHillsLayer",
|
||||
"MixinAddRiversLayer",
|
||||
"MixinBiomeSource",
|
||||
"MixinSetBaseBiomesLayer",
|
||||
"MixinMinecraftDedicatedServer",
|
||||
"MixinMultiNoiseBiomeSource",
|
||||
"MixinVanillaLayeredBiomeSource",
|
||||
"MultiNoiseBiomeSourceAccessor"
|
||||
"MixinSetBaseBiomesLayer",
|
||||
"MultiNoiseBiomeSourceAccessor",
|
||||
"VanillaLayeredBiomeSourceAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-biomes-v1",
|
||||
"name": "Fabric Biomes (v1)",
|
||||
"id": "fabric-biome-api-v1",
|
||||
"name": "Fabric Biome API (v1)",
|
||||
"version": "${version}",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/fabric-biomes-v1/icon.png",
|
||||
"icon": "assets/fabric-biome-api-v1/icon.png",
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net",
|
||||
"irc": "irc://irc.esper.net:6667/fabric",
|
||||
|
@ -16,11 +16,11 @@
|
|||
"FabricMC"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"minecraft": ">=1.16-alpha.20.22.a"
|
||||
"fabricloader": ">=0.9.2",
|
||||
"minecraft": ">=1.16.2"
|
||||
},
|
||||
"description": "Hooks for adding biomes to the default world generator.",
|
||||
"mixins": [
|
||||
"fabric-biomes-v1.mixins.json"
|
||||
"fabric-biome-api-v1.mixins.json"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.biome;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.biome.DefaultBiomeCreator;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
|
||||
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
|
||||
|
||||
/**
|
||||
* <b>NOTES FOR TESTING:</b>
|
||||
* When running with this test-mod, also test this when running a dedicated server since there
|
||||
* are significant differences between server + client and how they sync biomes.
|
||||
*
|
||||
* <p>Ingame, you can use <code>/locatebiome</code> since we use nether- and end-biomes in the overworld,
|
||||
* and vice-versa, making them easy to find to verify the injection worked.
|
||||
*
|
||||
* <p>If you don't find a biome right away, teleport far away (~10000 blocks) from spawn and try again.
|
||||
*/
|
||||
public class FabricBiomeTest implements ModInitializer {
|
||||
public static final String MOD_ID = "fabric-biome-api-v1-testmod";
|
||||
|
||||
private static final RegistryKey<Biome> TEST_CRIMSON_FOREST = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_crimson_forest"));
|
||||
|
||||
private static final RegistryKey<Biome> CUSTOM_PLAINS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "custom_plains"));
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Registry.register(BuiltinRegistries.BIOME, TEST_CRIMSON_FOREST.getValue(), DefaultBiomeCreator.createCrimsonForest());
|
||||
|
||||
NetherBiomes.addNetherBiome(BiomeKeys.BEACH, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.1F));
|
||||
NetherBiomes.addNetherBiome(TEST_CRIMSON_FOREST, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.275F));
|
||||
|
||||
Registry.register(BuiltinRegistries.BIOME, CUSTOM_PLAINS.getValue(), DefaultBiomeCreator.createPlains(false));
|
||||
OverworldBiomes.addBiomeVariant(BiomeKeys.PLAINS, CUSTOM_PLAINS, 1);
|
||||
|
||||
OverworldBiomes.addEdgeBiome(BiomeKeys.PLAINS, BiomeKeys.END_BARRENS, 0.9);
|
||||
|
||||
OverworldBiomes.addShoreBiome(BiomeKeys.FOREST, BiomeKeys.NETHER_WASTES, 0.9);
|
||||
|
||||
OverworldBiomes.addHillsBiome(BiomeKeys.BAMBOO_JUNGLE, BiomeKeys.BASALT_DELTAS, 0.9);
|
||||
|
||||
OverworldBiomes.addContinentalBiome(BiomeKeys.END_HIGHLANDS, OverworldClimate.DRY, 0.5);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-biomes-v1-testmod",
|
||||
"name": "Fabric Biomes (v1) Test Mod",
|
||||
"id": "fabric-biome-api-v1-testmod",
|
||||
"name": "Fabric Biome API (v1) Test Mod",
|
||||
"version": "1.0.0",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabric-biomes-v1": "*"
|
||||
"fabric-biome-api-v1": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
"main": [
|
|
@ -1,2 +0,0 @@
|
|||
archivesBaseName = "fabric-biomes-v1"
|
||||
version = getSubprojectVersion(project, "1.0.2")
|
|
@ -1,222 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.biome;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.Biomes;
|
||||
import net.minecraft.world.biome.layer.BiomeLayers;
|
||||
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
|
||||
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
|
||||
|
||||
/**
|
||||
* Lists and maps for internal use only! Stores data that is used by the various mixins into the world generation
|
||||
*/
|
||||
public final class InternalBiomeData {
|
||||
private InternalBiomeData() { }
|
||||
|
||||
private static final EnumMap<OverworldClimate, WeightedBiomePicker> OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS = new EnumMap<>(OverworldClimate.class);
|
||||
private static final Map<Biome, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new HashMap<>();
|
||||
private static final Map<Biome, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new HashMap<>();
|
||||
private static final Map<Biome, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new HashMap<>();
|
||||
private static final Map<Biome, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new HashMap<>();
|
||||
private static final Map<Biome, Biome> OVERWORLD_RIVER_MAP = new HashMap<>();
|
||||
|
||||
private static final Set<Biome> NETHER_BIOMES = new HashSet<>();
|
||||
private static final Map<Biome, Biome.MixedNoisePoint> NETHER_BIOME_NOISE_POINTS = new HashMap<>();
|
||||
|
||||
private static final Set<Biome> SPAWN_BIOMES = new HashSet<>();
|
||||
|
||||
private static Method injectBiomeMethod = null;
|
||||
|
||||
public static void addOverworldContinentalBiome(OverworldClimate climate, Biome biome, double weight) {
|
||||
Preconditions.checkArgument(climate != null, "Climate is null");
|
||||
Preconditions.checkArgument(biome != null, "Biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS.computeIfAbsent(climate, k -> new WeightedBiomePicker()).addBiome(biome, weight);
|
||||
injectOverworldBiome(biome);
|
||||
}
|
||||
|
||||
public static void addOverworldHillsBiome(Biome primary, Biome hills, double weight) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
Preconditions.checkArgument(hills != null, "Hills biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
OVERWORLD_HILLS_MAP.computeIfAbsent(primary, biome -> DefaultHillsData.injectDefaultHills(primary, new WeightedBiomePicker())).addBiome(hills, weight);
|
||||
injectOverworldBiome(hills);
|
||||
}
|
||||
|
||||
public static void addOverworldShoreBiome(Biome primary, Biome shore, double weight) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
Preconditions.checkArgument(shore != null, "Shore biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
OVERWORLD_SHORE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(shore, weight);
|
||||
injectOverworldBiome(shore);
|
||||
}
|
||||
|
||||
public static void addOverworldEdgeBiome(Biome primary, Biome edge, double weight) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
Preconditions.checkArgument(edge != null, "Edge biome is null");
|
||||
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
|
||||
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
|
||||
OVERWORLD_EDGE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(edge, weight);
|
||||
injectOverworldBiome(edge);
|
||||
}
|
||||
|
||||
public static void addOverworldBiomeReplacement(Biome replaced, Biome variant, double chance, OverworldClimate[] climates) {
|
||||
Preconditions.checkArgument(replaced != null, "Replaced biome is null");
|
||||
Preconditions.checkArgument(variant != null, "Variant biome is null");
|
||||
Preconditions.checkArgument(chance > 0 && chance <= 1, "Chance is not greater than 0 or less than or equal to 1");
|
||||
OVERWORLD_VARIANT_TRANSFORMERS.computeIfAbsent(replaced, biome -> new VariantTransformer()).addBiome(variant, chance, climates);
|
||||
injectOverworldBiome(variant);
|
||||
}
|
||||
|
||||
public static void setOverworldRiverBiome(Biome primary, Biome river) {
|
||||
Preconditions.checkArgument(primary != null, "Primary biome is null");
|
||||
OVERWORLD_RIVER_MAP.put(primary, river);
|
||||
|
||||
if (river != null) {
|
||||
injectOverworldBiome(river);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addSpawnBiome(Biome biome) {
|
||||
Preconditions.checkArgument(biome != null, "Biome is null");
|
||||
SPAWN_BIOMES.add(biome);
|
||||
}
|
||||
|
||||
private static void injectOverworldBiome(Biome biome) {
|
||||
try {
|
||||
if (injectBiomeMethod == null) {
|
||||
injectBiomeMethod = VanillaLayeredBiomeSource.class.getDeclaredMethod("fabric_injectBiome", Biome.class);
|
||||
injectBiomeMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
injectBiomeMethod.invoke(null, biome);
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addNetherBiome(Biome biome, Biome.MixedNoisePoint spawnNoisePoint) {
|
||||
Preconditions.checkArgument(biome != null, "Biome is null");
|
||||
Preconditions.checkArgument(spawnNoisePoint != null, "Biome.MixedNoisePoint is null");
|
||||
NETHER_BIOMES.add(biome);
|
||||
NETHER_BIOME_NOISE_POINTS.put(biome, spawnNoisePoint);
|
||||
}
|
||||
|
||||
public static Set<Biome> getSpawnBiomes() {
|
||||
return SPAWN_BIOMES;
|
||||
}
|
||||
|
||||
public static Map<Biome, WeightedBiomePicker> getOverworldHills() {
|
||||
return OVERWORLD_HILLS_MAP;
|
||||
}
|
||||
|
||||
public static Map<Biome, WeightedBiomePicker> getOverworldShores() {
|
||||
return OVERWORLD_SHORE_MAP;
|
||||
}
|
||||
|
||||
public static Map<Biome, WeightedBiomePicker> getOverworldEdges() {
|
||||
return OVERWORLD_EDGE_MAP;
|
||||
}
|
||||
|
||||
public static Map<Biome, Biome> getOverworldRivers() {
|
||||
return OVERWORLD_RIVER_MAP;
|
||||
}
|
||||
|
||||
public static EnumMap<OverworldClimate, WeightedBiomePicker> getOverworldModdedContinentalBiomePickers() {
|
||||
return OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS;
|
||||
}
|
||||
|
||||
public static Map<Biome, VariantTransformer> getOverworldVariantTransformers() {
|
||||
return OVERWORLD_VARIANT_TRANSFORMERS;
|
||||
}
|
||||
|
||||
public static Set<Biome> getNetherBiomes() {
|
||||
return Collections.unmodifiableSet(NETHER_BIOMES);
|
||||
}
|
||||
|
||||
public static Map<Biome, Biome.MixedNoisePoint> getNetherBiomeNoisePoints() {
|
||||
return NETHER_BIOME_NOISE_POINTS;
|
||||
}
|
||||
|
||||
private static class DefaultHillsData {
|
||||
private static final ImmutableMap<Biome, Biome> DEFAULT_HILLS;
|
||||
|
||||
static WeightedBiomePicker injectDefaultHills(Biome base, WeightedBiomePicker picker) {
|
||||
Biome defaultHill = DEFAULT_HILLS.get(base);
|
||||
|
||||
if (defaultHill != null) {
|
||||
picker.addBiome(defaultHill, 1);
|
||||
} else if (BiomeLayers.areSimilar(BuiltinRegistries.BIOME.getRawId(base), BuiltinRegistries.BIOME.getRawId(Biomes.WOODED_BADLANDS_PLATEAU))) {
|
||||
picker.addBiome(Biomes.BADLANDS, 1);
|
||||
} else if (base == Biomes.DEEP_OCEAN || base == Biomes.DEEP_LUKEWARM_OCEAN || base == Biomes.DEEP_COLD_OCEAN) {
|
||||
picker.addBiome(Biomes.PLAINS, 1);
|
||||
picker.addBiome(Biomes.FOREST, 1);
|
||||
} else if (base == Biomes.DEEP_FROZEN_OCEAN) {
|
||||
// Note: Vanilla Deep Frozen Oceans only have a 1/3 chance of having default hills.
|
||||
// This is a clever trick that ensures that when a mod adds hills with a weight of 1, the 1/3 chance is fulfilled.
|
||||
// 0.5 + 1.0 = 1.5, and 0.5 / 1.5 = 1/3.
|
||||
|
||||
picker.addBiome(Biomes.PLAINS, 0.25);
|
||||
picker.addBiome(Biomes.FOREST, 0.25);
|
||||
} else if (base == Biomes.PLAINS) {
|
||||
picker.addBiome(Biomes.WOODED_HILLS, 1);
|
||||
picker.addBiome(Biomes.FOREST, 2);
|
||||
}
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
static {
|
||||
ImmutableMap.Builder<Biome, Biome> builder = ImmutableMap.builder();
|
||||
builder.put(Biomes.DESERT, Biomes.DESERT_HILLS);
|
||||
builder.put(Biomes.FOREST, Biomes.WOODED_HILLS);
|
||||
builder.put(Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS);
|
||||
builder.put(Biomes.DARK_FOREST, Biomes.PLAINS);
|
||||
builder.put(Biomes.TAIGA, Biomes.TAIGA_HILLS);
|
||||
builder.put(Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS);
|
||||
builder.put(Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS);
|
||||
builder.put(Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS);
|
||||
builder.put(Biomes.JUNGLE, Biomes.JUNGLE_HILLS);
|
||||
builder.put(Biomes.BAMBOO_JUNGLE, Biomes.BAMBOO_JUNGLE_HILLS);
|
||||
builder.put(Biomes.OCEAN, Biomes.DEEP_OCEAN);
|
||||
builder.put(Biomes.LUKEWARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN);
|
||||
builder.put(Biomes.COLD_OCEAN, Biomes.DEEP_COLD_OCEAN);
|
||||
builder.put(Biomes.FROZEN_OCEAN, Biomes.DEEP_FROZEN_OCEAN);
|
||||
builder.put(Biomes.MOUNTAINS, Biomes.WOODED_MOUNTAINS);
|
||||
builder.put(Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU);
|
||||
DEFAULT_HILLS = builder.build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.BiomeSource;
|
||||
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
|
||||
/**
|
||||
* Adds spawn biomes to the base {@link BiomeSource} class.
|
||||
*/
|
||||
@Mixin(BiomeSource.class)
|
||||
public class MixinBiomeSource {
|
||||
@Shadow
|
||||
@Final
|
||||
private static List<Biome> SPAWN_BIOMES;
|
||||
|
||||
@Inject(at = @At("RETURN"), cancellable = true, method = "getSpawnBiomes")
|
||||
private void getSpawnBiomes(CallbackInfoReturnable<List<Biome>> info) {
|
||||
Set<Biome> biomes = new LinkedHashSet<>(info.getReturnValue());
|
||||
|
||||
if (biomes.addAll(InternalBiomeData.getSpawnBiomes())) {
|
||||
info.setReturnValue(new ArrayList<>(biomes));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
|
||||
import net.minecraft.world.gen.feature.StructureFeature;
|
||||
|
||||
/**
|
||||
* Adds the biomes in world gen to the array for the vanilla layered biome source.
|
||||
* This helps with {@link VanillaLayeredBiomeSource#hasStructureFeature(StructureFeature)} returning correctly for modded biomes as well as in {@link VanillaLayeredBiomeSource#getTopMaterials()}}
|
||||
*/
|
||||
@Mixin(VanillaLayeredBiomeSource.class)
|
||||
public class MixinVanillaLayeredBiomeSource {
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private static List<Biome> BIOMES;
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void cinit(CallbackInfo info) {
|
||||
BIOMES = new ArrayList<>(BIOMES);
|
||||
}
|
||||
|
||||
//Called via reflection
|
||||
private static void fabric_injectBiome(Biome biome) {
|
||||
BIOMES.add(biome);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.biome;
|
||||
|
||||
import net.minecraft.world.biome.DefaultBiomeCreator;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.Biomes;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.biomes.v1.NetherBiomes;
|
||||
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
|
||||
|
||||
public class FabricBiomeTest implements ModInitializer {
|
||||
public static final String MOD_ID = "fabric-biome-api-v1-testmod";
|
||||
|
||||
@Override public void onInitialize() {
|
||||
Biome biome = Registry.register(BuiltinRegistries.BIOME, new Identifier(MOD_ID, "test_crimson_forest"), DefaultBiomeCreator.createCrimsonForest());
|
||||
NetherBiomes.addNetherBiome(Biomes.BEACH, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.1F));
|
||||
NetherBiomes.addNetherBiome(biome, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.275F));
|
||||
|
||||
Biome customPlains = Registry.register(BuiltinRegistries.BIOME, new Identifier(MOD_ID, "custom_plains"), DefaultBiomeCreator.createPlains(null, false));
|
||||
OverworldBiomes.addBiomeVariant(Biomes.PLAINS, customPlains, 1);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-events-interaction-v0"
|
||||
version = getSubprojectVersion(project, "0.4.0")
|
||||
version = getSubprojectVersion(project, "0.4.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.block;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
@ -24,5 +26,5 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.world.BlockView;
|
||||
|
||||
public interface BlockPickInteractionAware {
|
||||
ItemStack getPickedStack(BlockState state, BlockView view, BlockPos pos, /* nullable */ PlayerEntity player, /* nullable */ HitResult result);
|
||||
ItemStack getPickedStack(BlockState state, BlockView view, BlockPos pos, @Nullable PlayerEntity player, @Nullable HitResult result);
|
||||
}
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.api.entity;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
|
||||
public interface EntityPickInteractionAware {
|
||||
ItemStack getPickedStack(/* nullable */ PlayerEntity player, /* nullable */ HitResult result);
|
||||
ItemStack getPickedStack(@Nullable PlayerEntity player, @Nullable HitResult result);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.event.player;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
@ -50,5 +52,5 @@ public interface AttackEntityCallback {
|
|||
}
|
||||
);
|
||||
|
||||
ActionResult interact(PlayerEntity player, World world, Hand hand, Entity entity, /* Nullable */ EntityHitResult hitResult);
|
||||
ActionResult interact(PlayerEntity player, World world, Hand hand, Entity entity, @Nullable EntityHitResult hitResult);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.event.player;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
@ -50,5 +52,5 @@ public interface UseEntityCallback {
|
|||
}
|
||||
);
|
||||
|
||||
ActionResult interact(PlayerEntity player, World world, Hand hand, Entity entity, /* Nullable */ EntityHitResult hitResult);
|
||||
ActionResult interact(PlayerEntity player, World world, Hand hand, Entity entity, @Nullable EntityHitResult hitResult);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-game-rule-api-v1"
|
||||
version = getSubprojectVersion(project, "1.0.1")
|
||||
version = getSubprojectVersion(project, "1.0.2")
|
||||
|
||||
minecraft {
|
||||
accessWidener = file("src/main/resources/fabric-game-rule-api-v1.accesswidener")
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.function.BiConsumer;
|
|||
|
||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.GameRules;
|
||||
|
@ -140,7 +141,7 @@ public final class GameRuleFactory {
|
|||
* @param changedCallback a callback that is invoked when the value of a game rule has changed
|
||||
* @return an integer rule type
|
||||
*/
|
||||
public static GameRules.Type<GameRules.IntRule> createIntRule(int defaultValue, int minimumValue, int maximumValue, /* @Nullable */ BiConsumer<MinecraftServer, GameRules.IntRule> changedCallback) {
|
||||
public static GameRules.Type<GameRules.IntRule> createIntRule(int defaultValue, int minimumValue, int maximumValue, @Nullable BiConsumer<MinecraftServer, GameRules.IntRule> changedCallback) {
|
||||
return new GameRules.Type<>(
|
||||
() -> IntegerArgumentType.integer(minimumValue, maximumValue),
|
||||
type -> new BoundedIntRule(type, defaultValue, minimumValue, maximumValue), // Internally use a bounded int rule
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
import com.mojang.brigadier.context.CommandContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
|
@ -144,7 +145,7 @@ public final class EnumRule<E extends Enum<E>> extends GameRules.Rule<EnumRule<E
|
|||
return this.supportedValues.contains(value);
|
||||
}
|
||||
|
||||
public void set(E value, /* @Nullable */ MinecraftServer server) throws IllegalArgumentException {
|
||||
public void set(E value, @Nullable MinecraftServer server) throws IllegalArgumentException {
|
||||
checkNotNull(value);
|
||||
|
||||
if (!this.supports(value)) {
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.gamerule;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.fabric.api.gamerule.v1.CustomGameRuleCategory;
|
||||
|
||||
public interface RuleKeyExtensions {
|
||||
/* @Nullable */
|
||||
@Nullable
|
||||
CustomGameRuleCategory fabric_getCustomCategory();
|
||||
|
||||
void fabric_setCustomCategory(CustomGameRuleCategory customCategory);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.gamerule;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
|
@ -26,8 +27,8 @@ import net.fabricmc.fabric.impl.gamerule.RuleKeyExtensions;
|
|||
|
||||
@Mixin(GameRules.Key.class)
|
||||
public abstract class RuleKeyMixin implements RuleKeyExtensions {
|
||||
/* @Nullable */
|
||||
@Unique
|
||||
@Nullable
|
||||
private CustomGameRuleCategory customCategory;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-item-api-v1"
|
||||
version = getSubprojectVersion(project, "1.1.0")
|
||||
version = getSubprojectVersion(project, "1.2.0")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.item.v1;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Allows an item to run custom logic when {@link ItemStack#damage(int, LivingEntity, Consumer)} is called.
|
||||
* This is useful for items that, for example, may drain durability from some other source before damaging
|
||||
* the stack itself.
|
||||
*
|
||||
* <p>Custom damage handlers can be set with {@link FabricItemSettings#customDamage}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CustomDamageHandler {
|
||||
/**
|
||||
* Called to apply damage to the given stack.
|
||||
* This can be used to e.g. drain from a battery before actually damaging the item.
|
||||
* @param amount The amount of damage originally requested
|
||||
* @param breakCallback Callback when the stack reaches zero damage. See {@link ItemStack#damage(int, LivingEntity, Consumer)} and its callsites for more information.
|
||||
* @return The amount of damage to pass to vanilla's logic
|
||||
*/
|
||||
int damage(ItemStack stack, int amount, LivingEntity entity, Consumer<LivingEntity> breakCallback);
|
||||
}
|
|
@ -19,6 +19,7 @@ package net.fabricmc.fabric.api.item.v1;
|
|||
import net.minecraft.item.FoodComponent;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Rarity;
|
||||
|
||||
import net.fabricmc.fabric.impl.item.FabricItemInternals;
|
||||
|
@ -42,6 +43,16 @@ public class FabricItemSettings extends Item.Settings {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom damage handler of the item.
|
||||
* Note that this is only called on an ItemStack if {@link ItemStack#isDamageable()} returns true.
|
||||
* @see CustomDamageHandler
|
||||
*/
|
||||
public FabricItemSettings customDamage(CustomDamageHandler handler) {
|
||||
FabricItemInternals.computeExtraData(this).customDamage(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Overrides of vanilla methods
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.WeakHashMap;
|
|||
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
|
||||
|
||||
public final class FabricItemInternals {
|
||||
|
@ -37,14 +38,20 @@ public final class FabricItemInternals {
|
|||
|
||||
if (data != null) {
|
||||
((ItemExtensions) item).fabric_setEquipmentSlotProvider(data.equipmentSlotProvider);
|
||||
((ItemExtensions) item).fabric_setCustomDamageHandler(data.customDamageHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ExtraData {
|
||||
private /* @Nullable */ EquipmentSlotProvider equipmentSlotProvider;
|
||||
private /* @Nullable */ CustomDamageHandler customDamageHandler;
|
||||
|
||||
public void equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
|
||||
this.equipmentSlotProvider = equipmentSlotProvider;
|
||||
}
|
||||
|
||||
public void customDamage(CustomDamageHandler handler) {
|
||||
this.customDamageHandler = handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.item;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
|
||||
|
||||
public interface ItemExtensions {
|
||||
EquipmentSlotProvider fabric_getEquipmentSlotProvider();
|
||||
/* @Nullable */ EquipmentSlotProvider fabric_getEquipmentSlotProvider();
|
||||
void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider);
|
||||
/* @Nullable */ CustomDamageHandler fabric_getCustomDamageHandler();
|
||||
void fabric_setCustomDamageHandler(CustomDamageHandler handler);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
|
||||
import net.fabricmc.fabric.impl.item.FabricItemInternals;
|
||||
import net.fabricmc.fabric.impl.item.ItemExtensions;
|
||||
|
@ -33,6 +34,9 @@ abstract class ItemMixin implements ItemExtensions {
|
|||
@Unique
|
||||
private EquipmentSlotProvider equipmentSlotProvider;
|
||||
|
||||
@Unique
|
||||
private CustomDamageHandler customDamageHandler;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onConstruct(Item.Settings settings, CallbackInfo info) {
|
||||
FabricItemInternals.onBuild(settings, (Item) (Object) this);
|
||||
|
@ -47,4 +51,14 @@ abstract class ItemMixin implements ItemExtensions {
|
|||
public void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider) {
|
||||
this.equipmentSlotProvider = equipmentSlotProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomDamageHandler fabric_getCustomDamageHandler() {
|
||||
return customDamageHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fabric_setCustomDamageHandler(CustomDamageHandler handler) {
|
||||
this.customDamageHandler = handler;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.item;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.impl.item.ItemExtensions;
|
||||
|
||||
@Mixin(ItemStack.class)
|
||||
public abstract class ItemStackMixin {
|
||||
@Shadow public abstract Item getItem();
|
||||
|
||||
@Unique
|
||||
private LivingEntity fabric_damagingEntity;
|
||||
|
||||
@Unique
|
||||
private Consumer<LivingEntity> fabric_breakCallback;
|
||||
|
||||
@Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At("HEAD"))
|
||||
private void saveDamager(int amount, LivingEntity entity, Consumer<LivingEntity> breakCallback, CallbackInfo ci) {
|
||||
this.fabric_damagingEntity = entity;
|
||||
this.fabric_breakCallback = breakCallback;
|
||||
}
|
||||
|
||||
@ModifyArg(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;damage(ILjava/util/Random;Lnet/minecraft/server/network/ServerPlayerEntity;)Z"), index = 0)
|
||||
private int hookDamage(int amount) {
|
||||
CustomDamageHandler handler = ((ItemExtensions) getItem()).fabric_getCustomDamageHandler();
|
||||
|
||||
if (handler != null) {
|
||||
return handler.damage((ItemStack) (Object) this, amount, fabric_damagingEntity, fabric_breakCallback);
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
@Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At("RETURN"))
|
||||
private <T extends LivingEntity> void clearDamager(int amount, T entity, Consumer<T> breakCallback, CallbackInfo ci) {
|
||||
this.fabric_damagingEntity = null;
|
||||
this.fabric_breakCallback = null;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
"package": "net.fabricmc.fabric.mixin.item",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"ItemStackMixin",
|
||||
"ItemMixin",
|
||||
"MobEntityMixin"
|
||||
],
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.item;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.PickaxeItem;
|
||||
import net.minecraft.item.ToolMaterials;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||
|
||||
public class CustomDamageTest implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "weird_pickaxe"), new WeirdPick());
|
||||
}
|
||||
|
||||
public static final CustomDamageHandler WEIRD_DAMAGE_HANDLER = (stack, amount, entity, breakCallback) -> {
|
||||
// If sneaking, apply all damage to vanilla. Otherwise, increment a tag on the stack by one and don't apply any damage
|
||||
if (entity.isSneaking()) {
|
||||
return amount;
|
||||
} else {
|
||||
CompoundTag tag = stack.getOrCreateTag();
|
||||
tag.putInt("weird", tag.getInt("weird") + 1);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
public static class WeirdPick extends PickaxeItem {
|
||||
protected WeirdPick() {
|
||||
super(ToolMaterials.GOLD, 1, -2.8F, new FabricItemSettings().customDamage(WEIRD_DAMAGE_HANDLER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getName(ItemStack stack) {
|
||||
int v = stack.getOrCreateTag().getInt("weird");
|
||||
return super.getName(stack).shallowCopy().append(" (Weird Value: " + v + ")");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "minecraft:item/golden_pickaxe"
|
||||
}
|
||||
}
|
|
@ -14,6 +14,9 @@
|
|||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.item.client.TooltipTests"
|
||||
],
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.item.CustomDamageTest"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-models-v0"
|
||||
version = getSubprojectVersion(project, "0.1.0")
|
||||
version = getSubprojectVersion(project, "0.1.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.client.model;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
|
@ -46,5 +48,6 @@ public interface ModelResourceProvider {
|
|||
* @return The loaded UnbakedModel, or null if this ModelResourceProvider doesn't handle a specific Identifier
|
||||
* (or if there was no error!).
|
||||
*/
|
||||
/* @Nullable */ UnbakedModel loadModelResource(Identifier resourceId, ModelProviderContext context) throws ModelProviderException;
|
||||
@Nullable
|
||||
UnbakedModel loadModelResource(Identifier resourceId, ModelProviderContext context) throws ModelProviderException;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.client.model;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
|
||||
|
@ -43,5 +45,6 @@ public interface ModelVariantProvider {
|
|||
* @return The loaded UnbakedModel, or null if this ModelVariantProvider doesn't handle a specific Identifier
|
||||
* (or if there was no error!).
|
||||
*/
|
||||
/* @Nullable */ UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) throws ModelProviderException;
|
||||
@Nullable
|
||||
UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) throws ModelProviderException;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.stream.Collectors;
|
|||
import com.google.common.collect.Lists;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
|
@ -139,12 +140,12 @@ public class ModelLoadingRegistryImpl implements ModelLoadingRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
/* @Nullable */
|
||||
@Nullable
|
||||
public UnbakedModel loadModelFromResource(Identifier resourceId) {
|
||||
return loadCustomModel((r) -> r.loadModelResource(resourceId, this), modelResourceProviders, "resource provider");
|
||||
}
|
||||
|
||||
/* @Nullable */
|
||||
@Nullable
|
||||
public UnbakedModel loadModelFromVariant(Identifier variantId) {
|
||||
if (!(variantId instanceof ModelIdentifier)) {
|
||||
return loadModelFromResource(variantId);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
archivesBaseName = "fabric-object-builder-api-v1"
|
||||
version = getSubprojectVersion(project, "1.7.0")
|
||||
version = getSubprojectVersion(project, "1.8.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
compile project(path: ':fabric-tool-attribute-api-v1', configuration: 'dev')
|
||||
|
||||
testmodCompile project(path: ':fabric-command-api-v1', configuration: 'dev')
|
||||
}
|
||||
|
||||
minecraft {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.object.builder.v1.advancement;
|
||||
|
||||
import net.minecraft.advancement.criterion.Criterion;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.mixin.object.builder.CriteriaAccessor;
|
||||
|
||||
/**
|
||||
* Allows registering advancement criteria for triggers.
|
||||
*
|
||||
* <p>A registered criterion (trigger) can be retrieved through
|
||||
* {@link net.minecraft.advancement.criterion.Criteria#getById(Identifier)}.</p>
|
||||
*
|
||||
* @see net.minecraft.advancement.criterion.Criteria
|
||||
*/
|
||||
public final class CriterionRegistry {
|
||||
/**
|
||||
* Registers a criterion for a trigger for advancements.
|
||||
*
|
||||
* @param <T> the criterion's type
|
||||
* @param criterion the criterion registered
|
||||
* @return the criterion registered, for chaining
|
||||
* @throws IllegalArgumentException if a criterion with the same {@link
|
||||
* Criterion#getId() id} exists
|
||||
*/
|
||||
public static <T extends Criterion<?>> T register(T criterion) {
|
||||
CriteriaAccessor.callRegister(criterion);
|
||||
return criterion;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.object.builder.v1.trade;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.village.TradeOffers;
|
||||
import net.minecraft.village.VillagerProfession;
|
||||
|
||||
import net.fabricmc.fabric.impl.object.builder.TradeOfferInternals;
|
||||
|
||||
/**
|
||||
* Utilities to help with registration of trade offers.
|
||||
*/
|
||||
public final class TradeOfferHelper {
|
||||
/**
|
||||
* Registers trade offer factories for use by villagers.
|
||||
*
|
||||
* <p>Below is an example, of registering a trade off factory to be added a blacksmith with a profession level of 3:
|
||||
* <blockquote><pre>
|
||||
* TradeOfferHelper.registerVillagerOffers(VillagerProfession.BLACKSMITH, 3, factories -> {
|
||||
* factories.add(new CustomTradeFactory(...);
|
||||
* });
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param profession the villager profession to assign the trades to
|
||||
* @param level the profession level the villager must be to offer the trades
|
||||
* @param factories a consumer to provide the factories
|
||||
*/
|
||||
public static void registerVillagerOffers(VillagerProfession profession, int level, Consumer<List<TradeOffers.Factory>> factories) {
|
||||
TradeOfferInternals.registerVillagerOffers(profession, level, factories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers trade offer factories for use by wandering trades.
|
||||
*
|
||||
* @param level the level the trades
|
||||
* @param factory a consumer to provide the factories
|
||||
*/
|
||||
public static void registerWanderingTraderOffers(int level, Consumer<List<TradeOffers.Factory>> factory) {
|
||||
TradeOfferInternals.registerWanderingTraderOffers(level, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the trade list by resetting the trade lists to vanilla state, and then registering all trade offers again.
|
||||
*
|
||||
* <p>This method is geared for use by mods which for example provide data driven villager trades.
|
||||
*/
|
||||
public static void refreshOffers() {
|
||||
TradeOfferInternals.refreshOffers();
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package net.fabricmc.fabric.api.object.builder.v1.villager;
|
|||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
|
@ -46,7 +47,7 @@ public final class VillagerProfessionBuilder {
|
|||
private final ImmutableSet.Builder<Block> secondaryJobSiteBlockBuilder = ImmutableSet.builder();
|
||||
private Identifier identifier;
|
||||
private PointOfInterestType pointOfInterestType;
|
||||
/* @Nullable */
|
||||
@Nullable
|
||||
private SoundEvent workSoundEvent;
|
||||
|
||||
private VillagerProfessionBuilder() {
|
||||
|
@ -54,6 +55,7 @@ public final class VillagerProfessionBuilder {
|
|||
|
||||
/**
|
||||
* Creates a builder instance to allow for creation of a {@link VillagerProfession}.
|
||||
*
|
||||
* @return A new builder.
|
||||
*/
|
||||
public static VillagerProfessionBuilder create() {
|
||||
|
@ -140,7 +142,7 @@ public final class VillagerProfessionBuilder {
|
|||
* @param workSoundEvent The {@link SoundEvent} to be played.
|
||||
* @return this builder.
|
||||
*/
|
||||
public VillagerProfessionBuilder workSound(/* @Nullable */ SoundEvent workSoundEvent) {
|
||||
public VillagerProfessionBuilder workSound(@Nullable SoundEvent workSoundEvent) {
|
||||
this.workSoundEvent = workSoundEvent;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package net.fabricmc.fabric.impl.object.builder;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.tag.Tag;
|
||||
|
@ -56,7 +58,8 @@ public final class FabricBlockInternals {
|
|||
|
||||
public static final class ExtraData {
|
||||
private final List<MiningLevel> miningLevels = new ArrayList<>();
|
||||
/* @Nullable */ private Boolean breakByHand;
|
||||
@Nullable
|
||||
private Boolean breakByHand;
|
||||
|
||||
public ExtraData(Block.Settings settings) {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.object.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import net.minecraft.village.TradeOffers;
|
||||
import net.minecraft.village.VillagerProfession;
|
||||
|
||||
import net.fabricmc.fabric.mixin.object.builder.TradeOffersAccessor;
|
||||
|
||||
public final class TradeOfferInternals {
|
||||
/**
|
||||
* A copy of the original trade offers map.
|
||||
*/
|
||||
public static Map<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> DEFAULT_VILLAGER_OFFERS;
|
||||
public static Int2ObjectMap<TradeOffers.Factory[]> DEFAULT_WANDERING_TRADER_OFFERS;
|
||||
private static final Map<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> VILLAGER_TRADE_FACTORIES = new HashMap<>();
|
||||
private static final Int2ObjectMap<TradeOffers.Factory[]> WANDERING_TRADER_FACTORIES = new Int2ObjectOpenHashMap<>();
|
||||
private TradeOfferInternals() {
|
||||
}
|
||||
|
||||
public static void registerVillagerOffers(VillagerProfession profession, int level, Consumer<List<TradeOffers.Factory>> factory) {
|
||||
final List<TradeOffers.Factory> list = new ArrayList<>();
|
||||
factory.accept(list);
|
||||
|
||||
final TradeOffers.Factory[] additionalEntries = list.toArray(new TradeOffers.Factory[0]);
|
||||
final Int2ObjectMap<TradeOffers.Factory[]> professionEntry = VILLAGER_TRADE_FACTORIES.computeIfAbsent(profession, p -> new Int2ObjectOpenHashMap<>());
|
||||
|
||||
final TradeOffers.Factory[] currentEntries = professionEntry.computeIfAbsent(level, l -> new TradeOffers.Factory[0]);
|
||||
final TradeOffers.Factory[] newEntries = ArrayUtils.addAll(additionalEntries, currentEntries);
|
||||
professionEntry.put(level, newEntries);
|
||||
|
||||
// Refresh the trades map
|
||||
TradeOfferInternals.refreshOffers();
|
||||
}
|
||||
|
||||
public static void registerWanderingTraderOffers(int level, Consumer<List<TradeOffers.Factory>> factory) {
|
||||
final List<TradeOffers.Factory> list = new ArrayList<>();
|
||||
factory.accept(list);
|
||||
|
||||
final TradeOffers.Factory[] additionalEntries = list.toArray(new TradeOffers.Factory[0]);
|
||||
final TradeOffers.Factory[] currentEntries = TradeOfferInternals.DEFAULT_WANDERING_TRADER_OFFERS.computeIfAbsent(level, key -> new TradeOffers.Factory[0]);
|
||||
|
||||
// Merge current and new entries
|
||||
final TradeOffers.Factory[] newEntries = ArrayUtils.addAll(additionalEntries, currentEntries);
|
||||
TradeOfferInternals.DEFAULT_WANDERING_TRADER_OFFERS.put(level, newEntries);
|
||||
|
||||
// Refresh the trades map
|
||||
TradeOfferInternals.refreshOffers();
|
||||
}
|
||||
|
||||
public static void refreshOffers() {
|
||||
TradeOfferInternals.refreshVillagerOffers();
|
||||
TradeOfferInternals.refreshWanderingTraderOffers();
|
||||
}
|
||||
|
||||
private static void refreshVillagerOffers() {
|
||||
final HashMap<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> trades = new HashMap<>(TradeOfferInternals.DEFAULT_VILLAGER_OFFERS);
|
||||
|
||||
for (Map.Entry<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> tradeFactoryEntry : TradeOfferInternals.VILLAGER_TRADE_FACTORIES.entrySet()) {
|
||||
// Create an empty map or get all existing profession entries.
|
||||
final Int2ObjectMap<TradeOffers.Factory[]> leveledFactoryMap = trades.computeIfAbsent(tradeFactoryEntry.getKey(), k -> new Int2ObjectOpenHashMap<>());
|
||||
// Get the existing entries
|
||||
final Int2ObjectMap<TradeOffers.Factory[]> value = tradeFactoryEntry.getValue();
|
||||
|
||||
// Iterate through the existing level entries
|
||||
for (int level : value.keySet()) {
|
||||
final TradeOffers.Factory[] factories = value.get(level);
|
||||
|
||||
if (factories != null) {
|
||||
final Int2ObjectMap<TradeOffers.Factory[]> resultMap = trades.computeIfAbsent(tradeFactoryEntry.getKey(), key -> new Int2ObjectOpenHashMap<>());
|
||||
resultMap.put(level, ArrayUtils.addAll(leveledFactoryMap.computeIfAbsent(level, key -> new TradeOffers.Factory[0]), factories));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new villager trade map
|
||||
TradeOffersAccessor.setVillagerTradeMap(trades);
|
||||
}
|
||||
|
||||
private static void refreshWanderingTraderOffers() {
|
||||
// Create an empty map that is a clone of the default offers
|
||||
final Int2ObjectMap<TradeOffers.Factory[]> trades = new Int2ObjectOpenHashMap<>(TradeOfferInternals.DEFAULT_WANDERING_TRADER_OFFERS);
|
||||
|
||||
for (int level : TradeOfferInternals.WANDERING_TRADER_FACTORIES.keySet()) {
|
||||
// Get all registered offers and add them to current entries
|
||||
final TradeOffers.Factory[] factories = TradeOfferInternals.WANDERING_TRADER_FACTORIES.get(level);
|
||||
trades.put(level, ArrayUtils.addAll(factories, trades.computeIfAbsent(level, key -> new TradeOffers.Factory[0])));
|
||||
}
|
||||
|
||||
// Set the new wandering trader trade map
|
||||
TradeOffersAccessor.setWanderingTraderTradeMap(trades);
|
||||
}
|
||||
|
||||
static {
|
||||
// Load the trade offers class so the field is set.
|
||||
TradeOffers.PROFESSION_TO_LEVELED_TRADE.getClass();
|
||||
}
|
||||
}
|
|
@ -14,18 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
package net.fabricmc.fabric.mixin.object.builder;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.advancement.criterion.Criteria;
|
||||
import net.minecraft.advancement.criterion.Criterion;
|
||||
|
||||
@Mixin(DimensionType.class)
|
||||
public interface DimensionTypeAccessor {
|
||||
@Invoker("createNetherGenerator")
|
||||
static ChunkGenerator createNetherGenerator(long seed) {
|
||||
throw new UnsupportedOperationException();
|
||||
@Mixin(Criteria.class)
|
||||
public interface CriteriaAccessor {
|
||||
@Invoker
|
||||
static <T extends Criterion<?>> T callRegister(T object) {
|
||||
throw new AssertionError("Mixin dummy");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.object.builder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.village.TradeOffers;
|
||||
import net.minecraft.village.VillagerProfession;
|
||||
|
||||
@Mixin(TradeOffers.class)
|
||||
public interface TradeOffersAccessor {
|
||||
@Accessor("PROFESSION_TO_LEVELED_TRADE")
|
||||
static void setVillagerTradeMap(Map<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> trades) {
|
||||
throw new AssertionError("This should not happen!");
|
||||
}
|
||||
|
||||
@Accessor("WANDERING_TRADER_TRADES")
|
||||
static void setWanderingTraderTradeMap(Int2ObjectMap<TradeOffers.Factory[]> trades) {
|
||||
throw new AssertionError("This should not happen!");
|
||||
}
|
||||
}
|
|
@ -14,28 +14,32 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.biome;
|
||||
package net.fabricmc.fabric.mixin.object.builder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.minecraft.server.dedicated.ServerPropertiesLoader;
|
||||
import net.minecraft.village.TradeOffers;
|
||||
import net.minecraft.village.VillagerProfession;
|
||||
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
|
||||
import net.fabricmc.fabric.impl.object.builder.TradeOfferInternals;
|
||||
|
||||
@Mixin(MinecraftDedicatedServer.class)
|
||||
public class MixinMinecraftDedicatedServer {
|
||||
@Mixin(TradeOffers.class)
|
||||
public abstract class TradeOffersMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private ServerPropertiesLoader propertiesLoader;
|
||||
public static Map<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> PROFESSION_TO_LEVELED_TRADE;
|
||||
@Shadow
|
||||
@Final
|
||||
public static Int2ObjectMap<TradeOffers.Factory[]> WANDERING_TRADER_TRADES;
|
||||
|
||||
@Inject(method = "setupServer", at = @At("HEAD"))
|
||||
public void setupServer(CallbackInfoReturnable<Boolean> cir) {
|
||||
InternalBiomeUtils.recreateChunkGenerators(propertiesLoader.getPropertiesHandler().generatorOptions);
|
||||
static {
|
||||
// Cache the original trade lists
|
||||
TradeOfferInternals.DEFAULT_VILLAGER_OFFERS = PROFESSION_TO_LEVELED_TRADE;
|
||||
TradeOfferInternals.DEFAULT_WANDERING_TRADER_OFFERS = WANDERING_TRADER_TRADES;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.object.builder;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.registry.DefaultedRegistry;
|
||||
import net.minecraft.village.TradeOffer;
|
||||
|
||||
@Mixin(targets = "net/minecraft/village/TradeOffers$TypeAwareBuyForOneEmeraldFactory")
|
||||
public abstract class TypeAwareTradeMixin {
|
||||
/**
|
||||
* Vanilla will check the "VillagerType -> Item" map in the stream and throw an exception for villager types not specified in the map.
|
||||
* This breaks any and all custom villager types.
|
||||
* We want to prevent this default logic so modded villager types will work.
|
||||
* So we return an empty stream so an exception is never thrown.
|
||||
*/
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DefaultedRegistry;stream()Ljava/util/stream/Stream;"))
|
||||
private <T> Stream<T> disableVanillaCheck(DefaultedRegistry<T> registry) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* To prevent "item" -> "air" trades, if the result of a type aware trade is air, make sure no offer is created.
|
||||
*/
|
||||
@Inject(method = "create(Lnet/minecraft/entity/Entity;Ljava/util/Random;)Lnet/minecraft/village/TradeOffer;", at = @At(value = "NEW", target = "net/minecraft/village/TradeOffer"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true)
|
||||
private void failOnNullItem(Entity entity, Random random, CallbackInfoReturnable<TradeOffer> cir, ItemStack buyingItem) {
|
||||
if (buyingItem.isEmpty()) { // Will return true for an "empty" item stack that had null passed in the ctor
|
||||
cir.setReturnValue(null); // Return null to prevent creation of empty trades
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.object.builder;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
@ -29,7 +30,7 @@ import net.minecraft.world.poi.PointOfInterestType;
|
|||
@Mixin(VillagerProfession.class)
|
||||
public interface VillagerProfessionAccessor {
|
||||
@Invoker("<init>")
|
||||
static VillagerProfession create(String id, PointOfInterestType type, ImmutableSet<Item> gatherableItems, ImmutableSet<Block> secondaryJobSites, /* @Nullable */ SoundEvent soundEvent) {
|
||||
static VillagerProfession create(String id, PointOfInterestType type, ImmutableSet<Item> gatherableItems, ImmutableSet<Block> secondaryJobSites, @Nullable SoundEvent soundEvent) {
|
||||
throw new AssertionError("Untransformed accessor!");
|
||||
}
|
||||
}
|
||||
|
|
Before ![]() (image error) Size: 1.5 KiB After ![]() (image error) Size: 1.5 KiB ![]() ![]() |
|
@ -6,12 +6,16 @@
|
|||
"AbstractBlockAccessor",
|
||||
"AbstractBlockSettingsAccessor",
|
||||
"AbstractBlockSettingsMixin",
|
||||
"CriteriaAccessor",
|
||||
"DefaultAttributeRegistryAccessor",
|
||||
"DefaultAttributeRegistryMixin",
|
||||
"MaterialBuilderAccessor",
|
||||
"MixinBlock",
|
||||
"PointOfInterestTypeAccessor",
|
||||
"SpawnRestrictionAccessor",
|
||||
"TradeOffersAccessor",
|
||||
"TradeOffersMixin",
|
||||
"TypeAwareTradeMixin",
|
||||
"VillagerProfessionAccessor"
|
||||
],
|
||||
"client": [
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.object.builder;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.advancement.criterion.ImpossibleCriterion;
|
||||
import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.object.builder.v1.advancement.CriterionRegistry;
|
||||
|
||||
public final class CriterionRegistryTest {
|
||||
public static void init() {
|
||||
CriterionRegistry.register(new CustomCriterion());
|
||||
}
|
||||
|
||||
static class CustomCriterion extends ImpossibleCriterion {
|
||||
static final Identifier ID = ObjectBuilderTestConstants.id("custom");
|
||||
|
||||
@Override
|
||||
public Identifier getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Conditions conditionsFromJson(JsonObject jsonObject, AdvancementEntityPredicateDeserializer advancementEntityPredicateDeserializer) {
|
||||
ObjectBuilderTestConstants.LOGGER.info("Loading custom criterion in advancement!");
|
||||
return super.conditionsFromJson(jsonObject, advancementEntityPredicateDeserializer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,24 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.biomes.v1;
|
||||
package net.fabricmc.fabric.test.object.builder;
|
||||
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* General API that applies to all biome sources.
|
||||
*/
|
||||
public final class FabricBiomes {
|
||||
private FabricBiomes() { }
|
||||
public final class ObjectBuilderTestConstants {
|
||||
public static final String MOD_ID = "fabric-object-builder-api-v1-testmod";
|
||||
public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
|
||||
|
||||
/**
|
||||
* Allows players to naturally spawn in this biome.
|
||||
*
|
||||
* @param biome a biome the player should be able to spawn in
|
||||
*/
|
||||
public static void addSpawnBiome(Biome biome) {
|
||||
InternalBiomeData.addSpawnBiome(biome);
|
||||
public static Identifier id(String name) {
|
||||
return new Identifier(MOD_ID, name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.object.builder;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.village.TradeOffer;
|
||||
import net.minecraft.village.TradeOffers;
|
||||
|
||||
class SimpleTradeFactory implements TradeOffers.Factory {
|
||||
private final TradeOffer offer;
|
||||
|
||||
SimpleTradeFactory(TradeOffer offer) {
|
||||
this.offer = offer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeOffer create(Entity entity, Random random) {
|
||||
// ALWAYS supply a copy of the offer.
|
||||
return new TradeOffer(this.offer.toTag());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.object.builder;
|
||||
|
||||
import static net.minecraft.command.argument.EntityArgumentType.entity;
|
||||
import static net.minecraft.command.argument.EntityArgumentType.getEntity;
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.passive.WanderingTraderEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.village.TradeOffer;
|
||||
import net.minecraft.village.TradeOffers;
|
||||
import net.minecraft.village.VillagerProfession;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper;
|
||||
|
||||
public class VillagerTypeTest1 implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
TradeOfferHelper.registerVillagerOffers(VillagerProfession.ARMORER, 1, factories -> {
|
||||
factories.add(new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.GOLD_INGOT, 3), new ItemStack(Items.NETHERITE_SCRAP, 4), new ItemStack(Items.NETHERITE_INGOT), 2, 6, 0.15F)));
|
||||
});
|
||||
|
||||
TradeOfferHelper.registerWanderingTraderOffers(1, factories -> {
|
||||
factories.add(new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.GOLD_INGOT, 3), new ItemStack(Items.NETHERITE_SCRAP, 4), new ItemStack(Items.NETHERITE_INGOT), 2, 6, 0.35F)));
|
||||
});
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
|
||||
dispatcher.register(literal("fabric_refreshtrades").executes(context -> {
|
||||
TradeOfferHelper.refreshOffers();
|
||||
context.getSource().sendFeedback(new LiteralText("Refreshed trades"), false);
|
||||
return 1;
|
||||
}));
|
||||
|
||||
dispatcher.register(literal("fabric_applywandering_trades")
|
||||
.then(argument("entity", entity()).executes(context -> {
|
||||
final Entity entity = getEntity(context, "entity");
|
||||
|
||||
if (!(entity instanceof WanderingTraderEntity)) {
|
||||
throw new SimpleCommandExceptionType(new LiteralText("Entity is not a wandering trader")).create();
|
||||
}
|
||||
|
||||
WanderingTraderEntity trader = (WanderingTraderEntity) entity;
|
||||
trader.getOffers().clear();
|
||||
|
||||
for (TradeOffers.Factory[] value : TradeOffers.WANDERING_TRADER_TRADES.values()) {
|
||||
for (TradeOffers.Factory factory : value) {
|
||||
final TradeOffer result = factory.create(trader, new Random());
|
||||
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
trader.getOffers().add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
})));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.object.builder;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.village.TradeOffer;
|
||||
import net.minecraft.village.VillagerProfession;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper;
|
||||
|
||||
/*
|
||||
* Second entrypoint to validate class loading does not break this.
|
||||
*/
|
||||
public class VillagerTypeTest2 implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
TradeOfferHelper.registerVillagerOffers(VillagerProfession.ARMORER, 1, factories -> {
|
||||
factories.add(new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.DIAMOND, 20), new ItemStack(Items.NETHERITE_INGOT), 3, 4, 0.15F)));
|
||||
});
|
||||
}
|
||||
}
|
Binary file not shown.
After ![]() (image error) Size: 1.5 KiB |
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "minecraft:command_block_minecart"
|
||||
},
|
||||
"title": {
|
||||
"text": "Criterion registry test advancement"
|
||||
},
|
||||
"description": {
|
||||
"text": "Criterion registry test advancement description"
|
||||
},
|
||||
"frame": "task",
|
||||
"show_toast": false,
|
||||
"announce_to_chat": false,
|
||||
"hidden": false,
|
||||
"background": "minecraft:textures/gui/advancements/backgrounds/stone.png"
|
||||
},
|
||||
"criteria": {
|
||||
"custom": {
|
||||
"trigger": "fabric-object-builder-api-v1-testmod:custom"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"custom"
|
||||
]
|
||||
]
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-object-builder-api-v1-testmod",
|
||||
"name": "Fabric Object Builder API (v1) Test Mod",
|
||||
"version": "${version}",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/fabric-object-builder-api-v1-testmod/icon.png",
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net",
|
||||
"irc": "irc://irc.esper.net:6667/fabric",
|
||||
"issues": "https://github.com/FabricMC/fabric/issues",
|
||||
"sources": "https://github.com/FabricMC/fabric"
|
||||
},
|
||||
"authors": [
|
||||
"FabricMC"
|
||||
],
|
||||
"depends": {
|
||||
"fabric-object-builder-api-v1": "*"
|
||||
},
|
||||
"description": "Test mod for fabric object builder API v1.",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.object.builder.CriterionRegistryTest::init",
|
||||
"net.fabricmc.fabric.test.object.builder.VillagerTypeTest1",
|
||||
"net.fabricmc.fabric.test.object.builder.VillagerTypeTest2"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-registry-sync-v0"
|
||||
version = getSubprojectVersion(project, "0.4.5")
|
||||
version = getSubprojectVersion(project, "0.5.2")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -27,11 +27,6 @@ public interface RegistryEntryAddedCallback<T> {
|
|||
void onEntryAdded(int rawId, Identifier id, T object);
|
||||
|
||||
static <T> Event<RegistryEntryAddedCallback<T>> event(Registry<T> registry) {
|
||||
if (!(registry instanceof ListenableRegistry)) {
|
||||
throw new IllegalArgumentException("Unsupported registry: " + registry.getClass().getName());
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return (Event<RegistryEntryAddedCallback<T>>) ((ListenableRegistry) registry).fabric_getAddObjectEvent();
|
||||
return ListenableRegistry.get(registry).fabric_getAddObjectEvent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,11 +27,6 @@ public interface RegistryEntryRemovedCallback<T> {
|
|||
void onEntryRemoved(int rawId, Identifier id, T object);
|
||||
|
||||
static <T> Event<RegistryEntryRemovedCallback<T>> event(Registry<T> registry) {
|
||||
if (!(registry instanceof ListenableRegistry)) {
|
||||
throw new IllegalArgumentException("Unsupported registry: " + registry.getClass().getName());
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return (Event<RegistryEntryRemovedCallback<T>>) ((ListenableRegistry) registry).fabric_getRemoveObjectEvent();
|
||||
return ListenableRegistry.get(registry).fabric_getRemoveObjectEvent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,11 +49,6 @@ public interface RegistryIdRemapCallback<T> {
|
|||
}
|
||||
|
||||
static <T> Event<RegistryIdRemapCallback<T>> event(Registry<T> registry) {
|
||||
if (!(registry instanceof ListenableRegistry)) {
|
||||
throw new IllegalArgumentException("Unsupported registry: " + registry.getClass().getName());
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return ((ListenableRegistry) registry).fabric_getRemapEvent();
|
||||
return ListenableRegistry.get(registry).fabric_getRemapEvent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.registry.sync;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.util.registry.MutableRegistry;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
|
||||
import net.fabricmc.fabric.mixin.registry.sync.AccessorRegistry;
|
||||
|
||||
/**
|
||||
* Handles synchronising changes to the built-in registries into the dynamic registry manager's template manager,
|
||||
* in case it gets classloaded early.
|
||||
*/
|
||||
public class DynamicRegistrySync {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
/**
|
||||
* Sets up a synchronisation that will propagate added entries to the given dynamic registry manager, which
|
||||
* should be the <em>built-in</em> manager. It is never destroyed. We don't ever have to unregister
|
||||
* the registry events.
|
||||
*/
|
||||
public static void setupSync(DynamicRegistryManager.Impl template) {
|
||||
LOGGER.debug("Setting up synchronisation of new BuiltinRegistries entries to the built-in DynamicRegistryManager");
|
||||
BuiltinRegistries.REGISTRIES.stream().forEach(source -> setupSync(source, template));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an event registration for the source registy that will ensure all entries added from now on
|
||||
* are also added to the template for dynamic registry managers.
|
||||
*/
|
||||
private static <T> void setupSync(Registry<T> source, DynamicRegistryManager.Impl template) {
|
||||
@SuppressWarnings("unchecked") AccessorRegistry<T> sourceAccessor = (AccessorRegistry<T>) source;
|
||||
RegistryKey<? extends Registry<T>> sourceKey = source.getKey();
|
||||
MutableRegistry<T> target = template.get(sourceKey);
|
||||
|
||||
RegistryEntryAddedCallback.event(source).register((rawId, id, object) -> {
|
||||
LOGGER.trace("Synchronizing {} from built-in registry {} into built-in dynamic registry manager template.",
|
||||
id, source.getKey());
|
||||
Lifecycle lifecycle = sourceAccessor.callGetEntryLifecycle(object);
|
||||
RegistryKey<T> entryKey = RegistryKey.of(sourceKey, id);
|
||||
target.set(rawId, entryKey, object, lifecycle);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.registry.sync;
|
||||
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
|
||||
|
@ -25,4 +27,13 @@ public interface ListenableRegistry<T> {
|
|||
Event<RegistryEntryAddedCallback<T>> fabric_getAddObjectEvent();
|
||||
Event<RegistryEntryRemovedCallback<T>> fabric_getRemoveObjectEvent();
|
||||
Event<RegistryIdRemapCallback<T>> fabric_getRemapEvent();
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> ListenableRegistry<T> get(Registry<T> registry) {
|
||||
if (!(registry instanceof ListenableRegistry)) {
|
||||
throw new IllegalArgumentException("Unsupported registry: " + registry.getKey().getValue());
|
||||
}
|
||||
|
||||
// Safe cast: this is implemented via Mixin and T will always match the T in Registry<T>
|
||||
return (ListenableRegistry<T>) registry;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.Packet;
|
||||
|
@ -105,7 +106,8 @@ public final class RegistrySyncManager {
|
|||
* @param activeTag contains the registry ids that were previously read and applied, can be null.
|
||||
* @return a {@link CompoundTag} to save or sync, null when empty
|
||||
*/
|
||||
public static CompoundTag toTag(boolean isClientSync, CompoundTag activeTag) {
|
||||
@Nullable
|
||||
public static CompoundTag toTag(boolean isClientSync, @Nullable CompoundTag activeTag) {
|
||||
CompoundTag mainTag = new CompoundTag();
|
||||
|
||||
for (Identifier registryId : Registry.REGISTRIES.getIds()) {
|
||||
|
|
|
@ -70,7 +70,7 @@ public final class StateIdTracker<T, S> implements RegistryIdRemapCallback<T>, R
|
|||
}
|
||||
|
||||
private void recalcStateMap() {
|
||||
((RemovableIdList) stateList).fabric_clear();
|
||||
((RemovableIdList<?>) stateList).fabric_clear();
|
||||
|
||||
Int2ObjectMap<T> sortedBlocks = new Int2ObjectRBTreeMap<>();
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.registry.sync;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import net.minecraft.util.registry.MutableRegistry;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
@ -32,4 +34,7 @@ public interface AccessorRegistry<T> {
|
|||
|
||||
@Accessor()
|
||||
RegistryKey<Registry<T>> getRegistryKey();
|
||||
|
||||
@Invoker
|
||||
Lifecycle callGetEntryLifecycle(T object);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import net.fabricmc.fabric.impl.registry.sync.trackers.vanilla.BlockItemTracker;
|
|||
|
||||
@Mixin(Bootstrap.class)
|
||||
public class MixinBootstrap {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Inject(method = "setOutputStreams", at = @At("RETURN"))
|
||||
private static void initialize(CallbackInfo info) {
|
||||
// These seemingly pointless accesses are done to make sure each
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.registry.sync;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
|
||||
import net.fabricmc.fabric.impl.registry.sync.DynamicRegistrySync;
|
||||
|
||||
@Mixin(DynamicRegistryManager.class)
|
||||
public class MixinDynamicRegistryManager {
|
||||
// This is the "template" for all subsequent built-in dynamic registry managers,
|
||||
// but it still contains the same objects as BuiltinRegistries, while the subsequent
|
||||
// managers built from this template will contain copies.
|
||||
@Shadow
|
||||
private static DynamicRegistryManager.Impl BUILTIN;
|
||||
|
||||
/**
|
||||
* Ensures that any registrations made into {@link net.minecraft.util.registry.BuiltinRegistries} after
|
||||
* {@link DynamicRegistryManager} has been class-loaded are still propagated.
|
||||
*/
|
||||
@Inject(method = "<clinit>", at = @At(value = "TAIL"))
|
||||
private static void setupBuiltInSync(CallbackInfo ci) {
|
||||
DynamicRegistrySync.setupSync(BUILTIN);
|
||||
}
|
||||
}
|
|
@ -31,13 +31,13 @@ import net.minecraft.util.collection.IdList;
|
|||
import net.fabricmc.fabric.impl.registry.sync.RemovableIdList;
|
||||
|
||||
@Mixin(IdList.class)
|
||||
public class MixinIdList implements RemovableIdList<Object> {
|
||||
public class MixinIdList<T> implements RemovableIdList<T> {
|
||||
@Shadow
|
||||
private int nextId;
|
||||
@Shadow
|
||||
private IdentityHashMap<Object, Integer> idMap;
|
||||
private IdentityHashMap<T, Integer> idMap;
|
||||
@Shadow
|
||||
private List<Object> list;
|
||||
private List<T> list;
|
||||
|
||||
@Override
|
||||
public void fabric_clear() {
|
||||
|
@ -47,7 +47,7 @@ public class MixinIdList implements RemovableIdList<Object> {
|
|||
}
|
||||
|
||||
@Unique
|
||||
private void fabric_removeInner(Object o) {
|
||||
private void fabric_removeInner(T o) {
|
||||
int value = idMap.remove(o);
|
||||
list.set(value, null);
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class MixinIdList implements RemovableIdList<Object> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void fabric_remove(Object o) {
|
||||
public void fabric_remove(T o) {
|
||||
if (idMap.containsKey(o)) {
|
||||
fabric_removeInner(o);
|
||||
}
|
||||
|
@ -65,9 +65,9 @@ public class MixinIdList implements RemovableIdList<Object> {
|
|||
|
||||
@Override
|
||||
public void fabric_removeId(int i) {
|
||||
List<Object> removals = new ArrayList<>();
|
||||
List<T> removals = new ArrayList<>();
|
||||
|
||||
for (Object o : idMap.keySet()) {
|
||||
for (T o : idMap.keySet()) {
|
||||
int j = idMap.get(o);
|
||||
|
||||
if (i == j) {
|
||||
|
@ -86,15 +86,15 @@ public class MixinIdList implements RemovableIdList<Object> {
|
|||
@Override
|
||||
public void fabric_remapIds(Int2IntMap map) {
|
||||
// remap idMap
|
||||
idMap.replaceAll((a, b) -> map.get(b));
|
||||
idMap.replaceAll((a, b) -> map.get((int) b));
|
||||
|
||||
// remap list
|
||||
nextId = 0;
|
||||
List<Object> oldList = new ArrayList<>(list);
|
||||
List<T> oldList = new ArrayList<>(list);
|
||||
list.clear();
|
||||
|
||||
for (int k = 0; k < oldList.size(); k++) {
|
||||
Object o = oldList.get(k);
|
||||
T o = oldList.get(k);
|
||||
|
||||
if (o != null) {
|
||||
int i = map.getOrDefault(k, k);
|
||||
|
|
|
@ -59,7 +59,7 @@ import net.fabricmc.fabric.impl.registry.sync.RemapStateImpl;
|
|||
import net.fabricmc.fabric.impl.registry.sync.RemappableRegistry;
|
||||
|
||||
@Mixin(SimpleRegistry.class)
|
||||
public abstract class MixinIdRegistry<T> implements RemappableRegistry, ListenableRegistry {
|
||||
public abstract class MixinIdRegistry<T> extends Registry<T> implements RemappableRegistry, ListenableRegistry<T> {
|
||||
@Shadow
|
||||
@Final
|
||||
private ObjectList<T> rawIdToEntry;
|
||||
|
@ -77,31 +77,32 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
@Unique
|
||||
private static Logger FABRIC_LOGGER = LogManager.getLogger();
|
||||
|
||||
public MixinIdRegistry(RegistryKey<? extends Registry<T>> key, Lifecycle lifecycle) {
|
||||
super(key, lifecycle);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private final Event<RegistryEntryAddedCallback> fabric_addObjectEvent = EventFactory.createArrayBacked(RegistryEntryAddedCallback.class,
|
||||
private final Event<RegistryEntryAddedCallback<T>> fabric_addObjectEvent = EventFactory.createArrayBacked(RegistryEntryAddedCallback.class,
|
||||
(callbacks) -> (rawId, id, object) -> {
|
||||
for (RegistryEntryAddedCallback callback : callbacks) {
|
||||
//noinspection unchecked
|
||||
for (RegistryEntryAddedCallback<T> callback : callbacks) {
|
||||
callback.onEntryAdded(rawId, id, object);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Unique
|
||||
private final Event<RegistryEntryRemovedCallback> fabric_removeObjectEvent = EventFactory.createArrayBacked(RegistryEntryRemovedCallback.class,
|
||||
private final Event<RegistryEntryRemovedCallback<T>> fabric_removeObjectEvent = EventFactory.createArrayBacked(RegistryEntryRemovedCallback.class,
|
||||
(callbacks) -> (rawId, id, object) -> {
|
||||
for (RegistryEntryRemovedCallback callback : callbacks) {
|
||||
//noinspection unchecked
|
||||
for (RegistryEntryRemovedCallback<T> callback : callbacks) {
|
||||
callback.onEntryRemoved(rawId, id, object);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Unique
|
||||
private final Event<RegistryIdRemapCallback> fabric_postRemapEvent = EventFactory.createArrayBacked(RegistryIdRemapCallback.class,
|
||||
private final Event<RegistryIdRemapCallback<T>> fabric_postRemapEvent = EventFactory.createArrayBacked(RegistryIdRemapCallback.class,
|
||||
(callbacks) -> (a) -> {
|
||||
for (RegistryIdRemapCallback callback : callbacks) {
|
||||
//noinspection unchecked
|
||||
for (RegistryIdRemapCallback<T> callback : callbacks) {
|
||||
callback.onRemap(a);
|
||||
}
|
||||
}
|
||||
|
@ -114,20 +115,17 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
|
||||
@Override
|
||||
public Event<RegistryEntryAddedCallback<T>> fabric_getAddObjectEvent() {
|
||||
//noinspection unchecked
|
||||
return (Event) fabric_addObjectEvent;
|
||||
return fabric_addObjectEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event<RegistryEntryRemovedCallback<T>> fabric_getRemoveObjectEvent() {
|
||||
//noinspection unchecked
|
||||
return (Event) fabric_removeObjectEvent;
|
||||
return fabric_removeObjectEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event<RegistryIdRemapCallback<T>> fabric_getRemapEvent() {
|
||||
//noinspection unchecked
|
||||
return (Event) fabric_postRemapEvent;
|
||||
return fabric_postRemapEvent;
|
||||
}
|
||||
|
||||
// The rest of the registry isn't thread-safe, so this one need not be either.
|
||||
|
@ -136,8 +134,8 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
@Inject(method = "set(ILnet/minecraft/util/registry/RegistryKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;Z)Ljava/lang/Object;", at = @At("HEAD"))
|
||||
public void setPre(int id, RegistryKey<T> registryId, Object object, Lifecycle lifecycle, boolean checkDuplicateKeys, CallbackInfoReturnable info) {
|
||||
int indexedEntriesId = entryToRawId.getInt((T) object);
|
||||
public void setPre(int id, RegistryKey<T> registryId, T object, Lifecycle lifecycle, boolean checkDuplicateKeys, CallbackInfoReturnable info) {
|
||||
int indexedEntriesId = entryToRawId.getInt(object);
|
||||
|
||||
if (indexedEntriesId >= 0) {
|
||||
throw new RuntimeException("Attempted to register object " + object + " twice! (at raw IDs " + indexedEntriesId + " and " + id + " )");
|
||||
|
@ -165,7 +163,7 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Inject(method = "set(ILnet/minecraft/util/registry/RegistryKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;Z)Ljava/lang/Object;", at = @At("RETURN"))
|
||||
public void setPost(int id, RegistryKey<T> registryId, Object object, Lifecycle lifecycle, boolean checkDuplicateKeys, CallbackInfoReturnable info) {
|
||||
public void setPost(int id, RegistryKey<T> registryId, T object, Lifecycle lifecycle, boolean checkDuplicateKeys, CallbackInfoReturnable info) {
|
||||
if (fabric_isObjectNew) {
|
||||
fabric_addObjectEvent.invoker().onEntryAdded(id, registryId.getValue(), object);
|
||||
}
|
||||
|
@ -173,9 +171,6 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
|
||||
@Override
|
||||
public void remap(String name, Object2IntMap<Identifier> remoteIndexedEntries, RemapMode mode) throws RemapException {
|
||||
//noinspection unchecked, ConstantConditions
|
||||
SimpleRegistry<Object> registry = (SimpleRegistry<Object>) (Object) this;
|
||||
|
||||
// Throw on invalid conditions.
|
||||
switch (mode) {
|
||||
case AUTHORITATIVE:
|
||||
|
@ -184,7 +179,7 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
List<String> strings = null;
|
||||
|
||||
for (Identifier remoteId : remoteIndexedEntries.keySet()) {
|
||||
if (!idToEntry.keySet().contains(remoteId)) {
|
||||
if (!idToEntry.containsKey(remoteId)) {
|
||||
if (strings == null) {
|
||||
strings = new ArrayList<>();
|
||||
}
|
||||
|
@ -210,13 +205,13 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
List<String> strings = new ArrayList<>();
|
||||
|
||||
for (Identifier remoteId : remoteIndexedEntries.keySet()) {
|
||||
if (!idToEntry.keySet().contains(remoteId)) {
|
||||
if (!idToEntry.containsKey(remoteId)) {
|
||||
strings.add(" - " + remoteId + " (missing on local)");
|
||||
}
|
||||
}
|
||||
|
||||
for (Identifier localId : registry.getIds()) {
|
||||
if (!remoteIndexedEntries.keySet().contains(localId)) {
|
||||
for (Identifier localId : getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(localId)) {
|
||||
strings.add(" - " + localId + " (missing on remote)");
|
||||
}
|
||||
}
|
||||
|
@ -244,15 +239,15 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
fabric_prevIndexedEntries = new Object2IntOpenHashMap<>();
|
||||
fabric_prevEntries = HashBiMap.create(idToEntry);
|
||||
|
||||
for (Object o : registry) {
|
||||
fabric_prevIndexedEntries.put(registry.getId(o), registry.getRawId(o));
|
||||
for (T o : this) {
|
||||
fabric_prevIndexedEntries.put(getId(o), getRawId(o));
|
||||
}
|
||||
}
|
||||
|
||||
Int2ObjectMap<Identifier> oldIdMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
for (Object o : registry) {
|
||||
oldIdMap.put(registry.getRawId(o), registry.getId(o));
|
||||
for (T o : this) {
|
||||
oldIdMap.put(getRawId(o), getId(o));
|
||||
}
|
||||
|
||||
// If we're AUTHORITATIVE, we append entries which only exist on the
|
||||
|
@ -270,7 +265,7 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
if (v > maxValue) maxValue = v;
|
||||
}
|
||||
|
||||
for (Identifier id : registry.getIds()) {
|
||||
for (Identifier id : getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(id)) {
|
||||
FABRIC_LOGGER.warn("Adding " + id + " to saved/remote registry.");
|
||||
remoteIndexedEntries.put(id, ++maxValue);
|
||||
|
@ -283,16 +278,15 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
// TODO: Is this what mods really want?
|
||||
Set<Identifier> droppedIds = new HashSet<>();
|
||||
|
||||
for (Identifier id : registry.getIds()) {
|
||||
for (Identifier id : getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(id)) {
|
||||
Object object = registry.get(id);
|
||||
int rid = registry.getRawId(object);
|
||||
T object = get(id);
|
||||
int rid = getRawId(object);
|
||||
|
||||
droppedIds.add(id);
|
||||
|
||||
// Emit RemoveObject events for removed objects.
|
||||
//noinspection unchecked
|
||||
fabric_getRemoveObjectEvent().invoker().onEntryRemoved(rid, id, (T) object);
|
||||
fabric_getRemoveObjectEvent().invoker().onEntryRemoved(rid, id, object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,9 +300,9 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
|
||||
Int2IntMap idMap = new Int2IntOpenHashMap();
|
||||
|
||||
for (Object o : rawIdToEntry) {
|
||||
Identifier id = registry.getId(o);
|
||||
int rid = registry.getRawId(o);
|
||||
for (T o : rawIdToEntry) {
|
||||
Identifier id = getId(o);
|
||||
int rid = getRawId(o);
|
||||
|
||||
// see above note
|
||||
if (remoteIndexedEntries.containsKey(id)) {
|
||||
|
@ -351,8 +345,7 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
}
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
fabric_getRemapEvent().invoker().onRemap(new RemapStateImpl(registry, oldIdMap, idMap));
|
||||
fabric_getRemapEvent().invoker().onRemap(new RemapStateImpl<>(this, oldIdMap, idMap));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -374,8 +367,8 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
idToEntry.putAll(fabric_prevEntries);
|
||||
|
||||
for (Map.Entry<Identifier, T> entry : fabric_prevEntries.entrySet()) {
|
||||
//noinspection unchecked
|
||||
keyToEntry.put(RegistryKey.of(RegistryKey.ofRegistry(((Registry) Registry.REGISTRIES).getId(this)), entry.getKey()), entry.getValue());
|
||||
RegistryKey<T> entryKey = RegistryKey.of(getKey(), entry.getKey());
|
||||
keyToEntry.put(entryKey, entry.getValue());
|
||||
}
|
||||
|
||||
remap(name, fabric_prevIndexedEntries, RemapMode.AUTHORITATIVE);
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.SimpleRegistry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
|
@ -58,8 +59,8 @@ public abstract class MixinSimpleRegistry<T> extends Registry<T> {
|
|||
RegistryAttributeHolder holder = RegistryAttributeHolder.get(this);
|
||||
|
||||
if (!holder.hasAttribute(RegistryAttribute.MODDED)) {
|
||||
// noinspection unchecked
|
||||
FARBIC_LOGGER.debug("Registry {} has been marked as modded, registry entry {} was changed", ((Registry) Registry.REGISTRIES).getId(this), registryKey.getValue());
|
||||
Identifier id = getKey().getValue();
|
||||
FARBIC_LOGGER.debug("Registry {} has been marked as modded, registry entry {} was changed", id, registryKey.getValue());
|
||||
RegistryAttributeHolder.get(this).addAttribute(RegistryAttribute.MODDED);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"mixins": [
|
||||
"AccessorRegistry",
|
||||
"MixinBootstrap",
|
||||
"MixinDynamicRegistryManager",
|
||||
"MixinIdList",
|
||||
"MixinIdRegistry",
|
||||
"MixinPlayerManager",
|
||||
|
|
|
@ -24,8 +24,14 @@ import net.minecraft.block.Material;
|
|||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.util.registry.MutableRegistry;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.SimpleRegistry;
|
||||
import net.minecraft.world.gen.feature.ConfiguredFeature;
|
||||
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.Feature;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.registry.DynamicRegistrySetupCallback;
|
||||
|
@ -43,6 +49,8 @@ public class RegistrySyncTest implements ModInitializer {
|
|||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
testBuiltInRegistrySync();
|
||||
|
||||
if (REGISTER_BLOCKS) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Block block = new Block(AbstractBlock.Settings.of(Material.STONE));
|
||||
|
@ -79,4 +87,53 @@ public class RegistrySyncTest implements ModInitializer {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that built-in registries are properly synchronized even after the dynamic reigstry managers have been
|
||||
* class-loaded.
|
||||
*/
|
||||
private void testBuiltInRegistrySync() {
|
||||
System.out.println("Checking built-in registry sync...");
|
||||
|
||||
// Register a configured feature before force-loading the dynamic registry manager
|
||||
ConfiguredFeature<DefaultFeatureConfig, ?> cf1 = Feature.BASALT_PILLAR.configure(DefaultFeatureConfig.INSTANCE);
|
||||
Identifier f1Id = new Identifier("registry_sync", "f1");
|
||||
Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, f1Id, cf1);
|
||||
|
||||
// Force-Initialize the dynamic registry manager, doing this in a Mod initializer would cause
|
||||
// further registrations into BuiltInRegistries to _NOT_ propagate into DynamicRegistryManager.BUILTIN
|
||||
checkFeature(DynamicRegistryManager.create(), f1Id);
|
||||
|
||||
ConfiguredFeature<DefaultFeatureConfig, ?> cf2 = Feature.DESERT_WELL.configure(DefaultFeatureConfig.INSTANCE);
|
||||
Identifier f2Id = new Identifier("registry_sync", "f2");
|
||||
Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, f2Id, cf2);
|
||||
|
||||
DynamicRegistryManager.Impl impl2 = DynamicRegistryManager.create();
|
||||
checkFeature(impl2, f1Id);
|
||||
checkFeature(impl2, f2Id);
|
||||
}
|
||||
|
||||
private void checkFeature(DynamicRegistryManager manager, Identifier id) {
|
||||
MutableRegistry<ConfiguredFeature<?, ?>> registry = manager.get(Registry.CONFIGURED_FEATURE_WORLDGEN);
|
||||
|
||||
ConfiguredFeature<?, ?> builtInEntry = BuiltinRegistries.CONFIGURED_FEATURE.get(id);
|
||||
|
||||
if (builtInEntry == null) {
|
||||
throw new IllegalStateException("Expected built-in entry to exist for: " + id);
|
||||
}
|
||||
|
||||
ConfiguredFeature<?, ?> entry = registry.get(id);
|
||||
|
||||
if (entry == null) {
|
||||
throw new IllegalStateException("Expected dynamic registry to contain entry " + id);
|
||||
}
|
||||
|
||||
if (builtInEntry == entry) {
|
||||
throw new IllegalStateException("Expected that the built-in entry and dynamic entry don't have object identity because the dynamic entry is created by serializing the built-in entry to JSON and back.");
|
||||
}
|
||||
|
||||
if (builtInEntry.feature != entry.feature) {
|
||||
throw new IllegalStateException("Expected both entries to reference the same feature since it's only in Registry and is never copied");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-renderer-api-v1"
|
||||
version = getSubprojectVersion(project, "0.3.0")
|
||||
version = getSubprojectVersion(project, "0.3.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.renderer.v1;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||
|
@ -50,6 +52,7 @@ public interface Renderer {
|
|||
* Return a material previously registered via {@link #registerMaterial(Identifier, RenderMaterial)}.
|
||||
* Will return null if no material was found matching the given identifier.
|
||||
*/
|
||||
@Nullable
|
||||
RenderMaterial materialById(Identifier id);
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.renderer.v1;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.fabric.impl.renderer.RendererAccessImpl;
|
||||
|
||||
/**
|
||||
|
@ -37,6 +39,7 @@ public interface RendererAccess {
|
|||
* Access to the current {@link Renderer} for creating and retrieving model builders
|
||||
* and materials. Will return null if no render plug in is active.
|
||||
*/
|
||||
@Nullable
|
||||
Renderer getRenderer();
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.api.renderer.v1.mesh;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.math.Vector3f;
|
||||
|
@ -115,7 +117,8 @@ public interface MutableQuadView extends QuadView {
|
|||
* is computed based on face geometry and must be non-null in vanilla quads.
|
||||
* That computed value is returned by {@link #lightFace()}.
|
||||
*/
|
||||
MutableQuadView cullFace(Direction face);
|
||||
@Nullable
|
||||
MutableQuadView cullFace(@Nullable Direction face);
|
||||
|
||||
/**
|
||||
* Provides a hint to renderer about the facing of this quad. Not required,
|
||||
|
@ -130,6 +133,7 @@ public interface MutableQuadView extends QuadView {
|
|||
* <p>Note: This value is not persisted independently when the quad is encoded.
|
||||
* When reading encoded quads, this value will always be the same as {@link #lightFace()}.
|
||||
*/
|
||||
@Nullable
|
||||
MutableQuadView nominalFace(Direction face);
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package net.fabricmc.fabric.api.renderer.v1.mesh;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
|
@ -77,6 +80,7 @@ public interface QuadView {
|
|||
* calculations and will be the block face to which the quad is most closely aligned. Always
|
||||
* the same as cull face for quads that are on a block face, but never null.
|
||||
*/
|
||||
@NotNull
|
||||
Direction lightFace();
|
||||
|
||||
/**
|
||||
|
@ -85,7 +89,7 @@ public interface QuadView {
|
|||
*
|
||||
* @see MutableQuadView#cullFace(Direction)
|
||||
*/
|
||||
Direction cullFace();
|
||||
@Nullable Direction cullFace();
|
||||
|
||||
/**
|
||||
* See {@link MutableQuadView#nominalFace(Direction)}.
|
||||
|
@ -135,7 +139,7 @@ public interface QuadView {
|
|||
* Pass a non-null target to avoid allocation - will be returned with values.
|
||||
* Otherwise returns a new instance.
|
||||
*/
|
||||
Vector3f copyPos(int vertexIndex, Vector3f target);
|
||||
Vector3f copyPos(int vertexIndex, @Nullable Vector3f target);
|
||||
|
||||
/**
|
||||
* Convenience: access x, y, z by index 0-2.
|
||||
|
@ -167,7 +171,8 @@ public interface QuadView {
|
|||
* Pass a non-null target to avoid allocation - will be returned with values.
|
||||
* Otherwise returns a new instance. Returns null if normal not present.
|
||||
*/
|
||||
Vector3f copyNormal(int vertexIndex, Vector3f target);
|
||||
@Nullable
|
||||
Vector3f copyNormal(int vertexIndex, @Nullable Vector3f target);
|
||||
|
||||
/**
|
||||
* Will return {@link Float#NaN} if normal not present.
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
|
@ -56,6 +57,7 @@ public abstract class ModelHelper {
|
|||
* optionally including the null face. (Use < or <= {@link #NULL_FACE_ID}
|
||||
* to exclude or include the null value, respectively.)
|
||||
*/
|
||||
@Contract("null -> null")
|
||||
public static Direction faceFromIndex(int faceIndex) {
|
||||
return FACES[faceIndex];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-renderer-indigo"
|
||||
version = getSubprojectVersion(project, "0.4.0")
|
||||
version = getSubprojectVersion(project, "0.4.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -18,6 +18,8 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.helper;
|
|||
|
||||
import static net.minecraft.util.math.MathHelper.approximatelyEquals;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.util.math.Vector3f;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
@ -114,7 +116,7 @@ public abstract class GeometryHelper {
|
|||
*
|
||||
* @param lightFace MUST be non-null.
|
||||
*/
|
||||
public static boolean isQuadCubic(Direction lightFace, QuadView quad) {
|
||||
public static boolean isQuadCubic(@NotNull Direction lightFace, QuadView quad) {
|
||||
if (lightFace == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue