*WIP*: Merge commit 'ce4cb8974f' into 1.18

This commit is contained in:
Frank 2021-11-13 19:30:01 +01:00
commit c6a7a1d4f7
76 changed files with 2754 additions and 738 deletions

View file

@ -13,7 +13,6 @@ import ru.bclib.api.dataexchange.handler.autosync.HelloClient;
import ru.bclib.api.dataexchange.handler.autosync.HelloServer;
import ru.bclib.api.dataexchange.handler.autosync.RequestFiles;
import ru.bclib.api.dataexchange.handler.autosync.SendFiles;
import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.config.Configs;
import ru.bclib.recipes.CraftingRecipes;
import ru.bclib.registry.BaseBlockEntities;
@ -53,8 +52,7 @@ public class BCLib implements ModInitializer {
Chunker.DESCRIPTOR
));
DataFixerAPI.registerPatch(() -> new BCLibPatch());
BCLibPatch.register();
Configs.save();
}

View file

@ -1,14 +1,23 @@
package ru.bclib;
import net.minecraft.nbt.CompoundTag;
import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.api.datafixer.Patch;
import ru.bclib.api.datafixer.PatchFunction;
public final class BCLibPatch extends Patch {
public final class BCLibPatch {
public static void register(){
DataFixerAPI.registerPatch(BiomeSourcePatch::new);
}
}
final class BiomeSourcePatch extends Patch {
private static final String NETHER_BIOME_SOURCE = "bclib:nether_biome_source";
private static final String END_BIOME_SOURCE = "bclib:end_biome_source";
protected BCLibPatch() {
protected BiomeSourcePatch() {
super(BCLib.MOD_ID, "0.4.0");
}

View file

@ -1,28 +1,42 @@
package ru.bclib.api;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
import net.fabricmc.fabric.impl.biome.NetherBiomeData;
import net.fabricmc.fabric.impl.biome.TheEndBiomeData;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
import net.fabricmc.fabric.mixin.biome.modification.GenerationSettingsAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biome.ClimateParameters;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
import org.jetbrains.annotations.Nullable;
import ru.bclib.util.MHelper;
import ru.bclib.world.biomes.BCLBiome;
import ru.bclib.world.biomes.FabricBiomesData;
import ru.bclib.world.features.BCLFeature;
import ru.bclib.world.generator.BiomePicker;
import ru.bclib.world.structures.BCLStructureFeature;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class BiomeAPI {
/**
@ -39,6 +53,9 @@ public class BiomeAPI {
private static final Map<Biome, BCLBiome> CLIENT = Maps.newHashMap();
private static Registry<Biome> biomeRegistry;
private static final Map<ResourceKey, List<BiConsumer<ResourceLocation, Biome>>> MODIFICATIONS = Maps.newHashMap();
private static final Set<ResourceLocation> MODIFIED_BIOMES = Sets.newHashSet();
public static final BCLBiome NETHER_WASTES_BIOME = registerNetherBiome(getFromRegistry(Biomes.NETHER_WASTES));
public static final BCLBiome CRIMSON_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.CRIMSON_FOREST));
public static final BCLBiome WARPED_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.WARPED_FOREST));
@ -325,4 +342,143 @@ public class BiomeAPI {
private static boolean pickerHasBiome(BiomePicker picker, ResourceLocation key) {
return picker.getBiomes().stream().filter(biome -> biome.getID().equals(key)).findFirst().isPresent();
}
/**
* Registers new biome modification for specified dimension. Will work both for mod and datapack biomes.
* @param dimensionID {@link ResourceLocation} dimension ID, example: Level.OVERWORLD or "minecraft:overworld".
* @param modification {@link BiConsumer} with {@link ResourceKey} biome ID and {@link Biome} parameters.
*/
public static void registerBiomeModification(ResourceKey dimensionID, BiConsumer<ResourceLocation, Biome> modification) {
List<BiConsumer<ResourceLocation, Biome>> modifications = MODIFICATIONS.get(dimensionID);
if (modifications == null) {
modifications = Lists.newArrayList();
MODIFICATIONS.put(dimensionID, modifications);
}
modifications.add(modification);
}
/**
* Registers new biome modification for the Overworld. Will work both for mod and datapack biomes.
* @param modification {@link BiConsumer} with {@link ResourceLocation} biome ID and {@link Biome} parameters.
*/
public static void registerOverworldBiomeModification(BiConsumer<ResourceLocation, Biome> modification) {
registerBiomeModification(Level.OVERWORLD, modification);
}
/**
* Registers new biome modification for the Nether. Will work both for mod and datapack biomes.
* @param modification {@link BiConsumer} with {@link ResourceLocation} biome ID and {@link Biome} parameters.
*/
public static void registerNetherBiomeModification(BiConsumer<ResourceLocation, Biome> modification) {
registerBiomeModification(Level.NETHER, modification);
}
/**
* Registers new biome modification for the End. Will work both for mod and datapack biomes.
* @param modification {@link BiConsumer} with {@link ResourceLocation} biome ID and {@link Biome} parameters.
*/
public static void registerEndBiomeModification(BiConsumer<ResourceLocation, Biome> modification) {
registerBiomeModification(Level.END, modification);
}
/**
* Will apply biome modifications to world, internal usage only.
* @param level
*/
public static void applyModifications(ServerLevel level) {
List<BiConsumer<ResourceLocation, Biome>> modifications = MODIFICATIONS.get(level.dimension());
if (modifications == null) {
return;
}
BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource();
List<Biome> biomes = source.possibleBiomes();
biomes.forEach(biome -> {
ResourceLocation biomeID = getBiomeID(biome);
boolean modify = isDatapackBiome(biomeID);
if (!modify && !MODIFIED_BIOMES.contains(biomeID)) {
MODIFIED_BIOMES.add(biomeID);
modify = true;
}
if (modify) {
modifications.forEach(consumer -> {
consumer.accept(biomeID, biome);
});
}
});
}
/**
* Adds new features to existing biome.
* @param biome {@link Biome} to add features in.
* @param feature {@link ConfiguredFeature} to add.
* @param step a {@link Decoration} step for the feature.
*/
public static void addBiomeFeature(Biome biome, ConfiguredFeature feature, Decoration step) {
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = getMutableList(accessor.fabric_getFeatures());
int index = step.ordinal();
if (biomeFeatures.size() < index) {
for (int i = biomeFeatures.size(); i <= index; i++) {
biomeFeatures.add(Lists.newArrayList());
}
}
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(biomeFeatures.get(index));
list.add(() -> feature);
accessor.fabric_setFeatures(biomeFeatures);
}
/**
* Adds new features to existing biome.
* @param biome {@link Biome} to add features in.
* @param features array of {@link BCLFeature} to add.
*/
public static void addBiomeFeatures(Biome biome, BCLFeature... features) {
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = getMutableList(accessor.fabric_getFeatures());
for (BCLFeature feature: features) {
int index = feature.getFeatureStep().ordinal();
if (biomeFeatures.size() < index) {
for (int i = biomeFeatures.size(); i <= index; i++) {
biomeFeatures.add(Lists.newArrayList());
}
}
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(biomeFeatures.get(index));
list.add(feature::getFeatureConfigured);
}
accessor.fabric_setFeatures(biomeFeatures);
}
/**
* Adds new structure feature to existing biome.
* @param biome {@link Biome} to add structure feature in.
* @param structure {@link ConfiguredStructureFeature} to add.
*/
public static void addBiomeStructure(Biome biome, ConfiguredStructureFeature structure) {
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
List<Supplier<ConfiguredStructureFeature<?, ?>>> biomeStructures = getMutableList(accessor.fabric_getStructureFeatures());
biomeStructures.add(() -> structure);
accessor.fabric_setStructureFeatures(biomeStructures);
}
/**
* Adds new structure features to existing biome.
* @param biome {@link Biome} to add structure features in.
* @param structures array of {@link BCLStructureFeature} to add.
*/
public static void addBiomeStructures(Biome biome, BCLStructureFeature... structures) {
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
List<Supplier<ConfiguredStructureFeature<?, ?>>> biomeStructures = getMutableList(accessor.fabric_getStructureFeatures());
for (BCLStructureFeature structure: structures) {
biomeStructures.add(structure::getFeatureConfigured);
}
accessor.fabric_setStructureFeatures(biomeStructures);
}
private static <T extends Object> List<T> getMutableList(List<T> input) {
if (input instanceof ImmutableList) {
return Lists.newArrayList(input);
}
return input;
}
}

View file

@ -34,9 +34,16 @@ public class TagAPI {
public static final Tag.Named<Block> BLOCK_END_GROUND = makeBlockTag(BCLib.MOD_ID, "end_ground");
public static final Tag.Named<Block> BLOCK_CHEST = makeCommonBlockTag("chest");
public static final Tag.Named<Block> BLOCK_WOODEN_CHEST = makeCommonBlockTag("wooden_chests");
public static final Tag.Named<Block> BLOCK_BARREL = makeCommonBlockTag("barrel");
public static final Tag.Named<Block> BLOCK_WOODEN_BARREL = makeCommonBlockTag("wooden_barrels");
public static final Tag.Named<Block> BLOCK_END_STONES = makeCommonBlockTag("end_stones");
public static final Tag.Named<Block> BLOCK_NETHER_STONES = makeCommonBlockTag("nether_stones");
public static final Tag.Named<Block> BLOCK_WORKBENCHES = makeCommonBlockTag("workbenches");
public static final Tag.Named<Block> BLOCK_NETHER_PORTAL_FRAME = makeCommonBlockTag("nether_pframe");
public static final Tag.Named<Block> BLOCK_WORKBENCHES = makeCommonBlockTag("workbench");
public static final Tag.Named<Block> BLOCK_SAPLINGS = makeCommonBlockTag("saplings");
public static final Tag.Named<Block> BLOCK_LEAVES = makeCommonBlockTag("leaves");
public static final Tag.Named<Block> BLOCK_IMMOBILE = makeCommonBlockTag("immobile");
public static final Tag.Named<Block> BLOCK_DRAGON_IMMUNE = getMCBlockTag("dragon_immune");
@ -47,11 +54,19 @@ public class TagAPI {
// Item Tags
public static final Tag.Named<Item> ITEM_CHEST = makeCommonItemTag("chest");
public static final Tag.Named<Item> ITEM_WOODEN_CHEST = makeCommonItemTag("wooden_chests");
public static final Tag.Named<Item> ITEM_BARREL = makeCommonItemTag("barrel");
public static final Tag.Named<Item> ITEM_WOODEN_BARREL = makeCommonItemTag("wooden_barrels");
public static final Tag.Named<Item> ITEM_IRON_INGOTS = makeCommonItemTag("iron_ingots");
public static final Tag.Named<Item> ITEM_FURNACES = makeCommonItemTag("furnaces");
public static final Tag.Named<Item> ITEM_WORKBENCHES = makeCommonItemTag("workbenches");
public static final Tag.Named<Item> ITEM_WORKBENCHES = makeCommonItemTag("workbench");
public final static Tag.Named<Item> ITEM_HAMMERS = makeCommonItemTag("hammers");
public static final Tag.Named<Item> ITEM_SAPLINGS = makeCommonItemTag("saplings");
public static final Tag.Named<Item> ITEM_LEAVES = makeCommonItemTag("leaves");
public static final Tag.Named<Item> ITEM_SHEARS = getMCItemTag("shears");
public static final Tag.Named<Item> ITEM_COMMON_SHEARS = makeCommonItemTag("shears");
/**
* Get or create {@link Tag.Named}.
*
@ -122,6 +137,18 @@ public class TagAPI {
return tag == null ? (Named<Block>) TagFactory.BLOCK.create(id): (Named<Block>) tag;
}
/**
* Get or create Minecraft {@link Item} {@link Tag.Named}.
*
* @param name - {@link String} tag name.
* @return {@link Item} {@link Tag.Named}.
*/
public static Tag.Named<Item> getMCItemTag(String name) {
ResourceLocation id = new ResourceLocation(name);
Tag<Item> tag = ItemTags.getAllTags().getTag(id);
return tag == null ? (Named<Item>) TagRegistry.item(id) : (Named<Item>) tag;
}
/**
* Adds {@link Block} to NETHER_GROUND and GEN_TERRAIN tags to process it properly in terrain generators and block logic.
*

View file

@ -135,7 +135,7 @@ public class AutoSync {
}
private static boolean didRegisterAdditionalMods = false;
//we call this from HelloClient on the Srver to prepare transfer
//we call this from HelloClient on the Server to prepare transfer
protected static void loadSyncFolder() {
if (Configs.SERVER_CONFIG.isOfferingFiles()) {
syncFolderDescriptions.forEach(desc -> desc.loadCache());

View file

@ -4,10 +4,10 @@ import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ProgressListener;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.ProgressListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.bclib.BCLib;

View file

@ -16,6 +16,7 @@ import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID.WithContentOverride;
import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile;
import ru.bclib.config.Configs;
import ru.bclib.config.ServerConfig;
import ru.bclib.gui.screens.ModListScreen;
import ru.bclib.gui.screens.ProgressScreen;
import ru.bclib.gui.screens.SyncFilesScreen;
@ -91,6 +92,11 @@ public class HelloClient extends DataHandler.FromServer {
.collect(Collectors.toList())
);
}
mods = mods
.stream()
.filter(entry -> !Configs.SERVER_CONFIG.get(ServerConfig.EXCLUDED_MODS).contains(entry))
.collect(Collectors.toList());
//write Plugin Versions
buf.writeInt(mods.size());
@ -394,6 +400,9 @@ public class HelloClient extends DataHandler.FromServer {
requestBCLibDownload();
this.onCloseSyncFilesScreen();
} else {
Minecraft.getInstance()
.setScreen(null);
}
}));
}

View file

@ -25,6 +25,8 @@ import ru.bclib.api.WorldDataAPI;
import ru.bclib.config.Configs;
import ru.bclib.gui.screens.AtomicProgressListener;
import ru.bclib.gui.screens.ConfirmFixScreen;
import ru.bclib.gui.screens.LevelFixErrorScreen;
import ru.bclib.gui.screens.LevelFixErrorScreen.Listener;
import ru.bclib.gui.screens.ProgressScreen;
import ru.bclib.util.Logger;
@ -39,6 +41,7 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.zip.ZipException;
/**
* API to manage Patches that need to get applied to a world
@ -47,6 +50,24 @@ public class DataFixerAPI {
static final Logger LOGGER = new Logger("DataFixerAPI");
static class State {
public boolean didFail = false;
protected ArrayList<String> errors = new ArrayList<>();
public void addError(String s){
errors.add(s);
}
public boolean hasError(){
return errors.size()>0;
}
public String getErrorMessage(){
return errors.stream().reduce("", (a, b) -> a + " - " + b + "\n");
}
public String[] getErrorMessages(){
String[] res = new String[errors.size()];
return errors.toArray(res);
}
}
@FunctionalInterface
@ -200,30 +221,48 @@ public class DataFixerAPI {
progress = null;
}
Runnable runner = () -> {
Supplier<State> runner = () -> {
if (createBackup) {
progress.progressStage(new TranslatableComponent("message.bclib.datafixer.progress.waitbackup"));
EditWorldScreen.makeBackupAndShowToast(Minecraft.getInstance().getLevelSource(), levelID);
}
if (applyFixes) {
runDataFixes(dir, profile, progress);
return runDataFixes(dir, profile, progress);
}
return new State();
};
if (showUI) {
Thread fixerThread = new Thread(() -> {
runner.run();
Minecraft.getInstance().execute(() -> {
if (profile != null && showUI) {
onResume.accept(applyFixes);
}
});
final State state = runner.get();
Minecraft.getInstance()
.execute(() -> {
if (profile != null && showUI) {
//something went wrong, show the user our error
if (state.didFail || state.hasError()){
showLevelFixErrorScreen(state, (markFixed)->{
if (markFixed) {
profile.markApplied();
}
onResume.accept(applyFixes);
});
} else {
onResume.accept(applyFixes);
}
}
});
});
fixerThread.start();
} else {
runner.run();
State state = runner.get();
if (state.hasError()){
LOGGER.error("There were Errors while fixing the Level:");
LOGGER.error(state.getErrorMessage());
}
}
};
@ -240,6 +279,11 @@ public class DataFixerAPI {
}
return false;
}
@Environment(EnvType.CLIENT)
private static void showLevelFixErrorScreen(State state, Listener onContinue){
Minecraft.getInstance()
.setScreen(new LevelFixErrorScreen(Minecraft.getInstance().screen, state.getErrorMessages(), onContinue));
}
private static MigrationProfile loadProfileIfNeeded(File levelBaseDir){
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) {
@ -270,7 +314,7 @@ public class DataFixerAPI {
Minecraft.getInstance().setScreen(new ConfirmFixScreen((Screen) null, whenFinished::accept));
}
private static void runDataFixes(File dir, MigrationProfile profile, AtomicProgressListener progress) {
private static State runDataFixes(File dir, MigrationProfile profile, AtomicProgressListener progress) {
State state = new State();
progress.resetAtomic();
@ -295,6 +339,7 @@ public class DataFixerAPI {
profile.patchWorldData();
} catch (PatchDidiFailException e){
state.didFail = true;
state.addError("Failed fixing worldconfig (" + e.getMessage() + ")");
BCLib.LOGGER.error(e.getMessage());
}
progress.incAtomic(maxProgress);
@ -313,6 +358,8 @@ public class DataFixerAPI {
progress.incAtomic(maxProgress);
progress.stop();
return state;
}
private static void fixLevel(MigrationProfile profile, State state, File levelBaseDir) {
@ -342,15 +389,17 @@ public class DataFixerAPI {
}
catch (Exception e) {
BCLib.LOGGER.error("Failed fixing Level-Data.");
state.addError("Failed fixing Level-Data in level.dat (" + e.getMessage() + ")");
state.didFail = true;
e.printStackTrace();
}
}
private static void fixPlayer(MigrationProfile data, State state, File file) {
try {
LOGGER.info("Inspecting " + file);
CompoundTag player = NbtIo.readCompressed(file);
CompoundTag player = readNbt(file);
boolean[] changed = { false };
fixPlayerNbt(player, changed, data);
@ -361,11 +410,12 @@ public class DataFixerAPI {
}
catch (Exception e) {
BCLib.LOGGER.error("Failed fixing Player-Data.");
state.addError("Failed fixing Player-Data in " + file.getName() + " (" + e.getMessage() + ")");
state.didFail = true;
e.printStackTrace();
}
}
private static void fixPlayerNbt(CompoundTag player, boolean[] changed, MigrationProfile data) {
//Checking Inventory
ListTag inventory = player.getList("Inventory", Tag.TAG_COMPOUND);
@ -416,7 +466,7 @@ public class DataFixerAPI {
for (int z = 0; z < 32; z++) {
ChunkPos pos = new ChunkPos(x, z);
changed[0] = false;
if (region.hasChunk(pos)) {
if (region.hasChunk(pos) && !state.didFail) {
DataInputStream input = region.getChunkDataInputStream(pos);
CompoundTag root = NbtIo.read(input);
// if ((root.toString().contains("betternether:chest") || root.toString().contains("bclib:chest"))) {
@ -442,6 +492,17 @@ public class DataFixerAPI {
CompoundTag blockTagCompound = ((CompoundTag) blockTag);
changed[0] |= data.replaceStringFromIDs(blockTagCompound, "Name");
});
try {
changed[0] |= data.patchBlockState(palette, ((CompoundTag) tag).getList("BlockStates", Tag.TAG_LONG));
}
catch (PatchDidiFailException e) {
BCLib.LOGGER.error("Failed fixing BlockState in " + pos);
state.addError("Failed fixing BlockState in " + pos + " (" + e.getMessage() + ")");
state.didFail = true;
changed[0] = false;
e.printStackTrace();
}
});
if (changed[0]) {
@ -450,7 +511,6 @@ public class DataFixerAPI {
DataOutputStream output = region.getChunkDataOutputStream(pos);
NbtIo.write(root, output);
output.close();
}
}
}
@ -458,12 +518,13 @@ public class DataFixerAPI {
region.close();
}
catch (Exception e) {
BCLib.LOGGER.error("Failed fixing Player Data.");
BCLib.LOGGER.error("Failed fixing Region.");
state.addError("Failed fixing Region in " + file.getName() + " (" + e.getMessage() + ")");
state.didFail = true;
e.printStackTrace();
}
}
static CompoundTag patchConfTag = null;
static CompoundTag getPatchData(){
if (patchConfTag==null) {
@ -540,4 +601,12 @@ public class DataFixerAPI {
Patch.getALL().add(patch.get());
}
private static CompoundTag readNbt(File file) throws IOException {
try {
return NbtIo.readCompressed(file);
} catch (ZipException e){
return NbtIo.read(file);
}
}
}

View file

@ -0,0 +1,47 @@
package ru.bclib.api.datafixer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A Patch for level.dat that is always executed no matter what Patchlevel is set in a world.
*/
public abstract class ForcedLevelPatch extends Patch {
protected ForcedLevelPatch(@NotNull String modID, String version) {
super(modID, version, true);
}
@Override
public final Map<String, String> getIDReplacements() {
return new HashMap<String, String>();
}
@Override
public final PatchFunction<CompoundTag, Boolean> getWorldDataPatcher() { return null; }
@Override
public final PatchBiFunction<ListTag, ListTag, Boolean> getBlockStatePatcher() { return null; }
@Override
public final List<String> getWorldDataIDPaths() {
return null;
}
@Override
public PatchFunction<CompoundTag, Boolean> getLevelDatPatcher() { return this::runLevelDatPatch; }
/**
* Called with the contents of level.dat in {@code root}
* @param root The contents of level.dat
* @param profile The active migration profile
* @return true, if the run did change the contents of root
*/
abstract protected Boolean runLevelDatPatch(CompoundTag root, MigrationProfile profile);
}

View file

@ -11,6 +11,7 @@ import ru.bclib.util.ModUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@ -23,6 +24,7 @@ public class MigrationProfile {
final Set<String> mods;
final Map<String, String> idReplacements;
final List<PatchFunction<CompoundTag, Boolean>> levelPatchers;
final List<PatchBiFunction<ListTag, ListTag, Boolean>> statePatchers;
final List<Patch> worldDataPatchers;
final Map<String, List<String>> worldDataIDPaths;
@ -33,7 +35,7 @@ public class MigrationProfile {
private boolean didRunPrePatch;
private Exception prePatchException;
MigrationProfile(CompoundTag config) {
MigrationProfile(CompoundTag config, boolean applyAll) {
this.config = config;
this.mods = Collections.unmodifiableSet(Patch.getALL()
@ -44,6 +46,7 @@ public class MigrationProfile {
HashMap<String, String> replacements = new HashMap<String, String>();
List<PatchFunction<CompoundTag, Boolean>> levelPatches = new LinkedList<>();
List<Patch> worldDataPatches = new LinkedList<>();
List<PatchBiFunction<ListTag, ListTag, Boolean>> statePatches = new LinkedList<>();
HashMap<String, List<String>> worldDataIDPaths = new HashMap<>();
for (String modID : mods) {
@ -54,12 +57,14 @@ public class MigrationProfile {
List<String> paths = patch.getWorldDataIDPaths();
if (paths!=null) worldDataIDPaths.put(modID, paths);
if (currentPatchLevel(modID) < patch.level) {
if (applyAll || currentPatchLevel(modID) < patch.level || patch.alwaysApply) {
replacements.putAll(patch.getIDReplacements());
if (patch.getLevelDatPatcher()!=null)
levelPatches.add(patch.getLevelDatPatcher());
if (patch.getWorldDataPatcher()!=null)
worldDataPatches.add(patch);
if (patch.getBlockStatePatcher()!=null)
statePatches.add(patch.getBlockStatePatcher());
DataFixerAPI.LOGGER.info("Applying " + patch);
}
else {
@ -72,6 +77,54 @@ public class MigrationProfile {
this.idReplacements = Collections.unmodifiableMap(replacements);
this.levelPatchers = Collections.unmodifiableList(levelPatches);
this.worldDataPatchers = Collections.unmodifiableList(worldDataPatches);
this.statePatchers = Collections.unmodifiableList(statePatches);
}
/**
* This method is supposed to be used by developers to apply id-patches to custom nbt structures. It is only
* available in Developer-Mode
*
*/
public static void fixCustomFolder(File dir){
if (!BCLib.isDevEnvironment()) return;
MigrationProfile profile = Patch.createMigrationData();
List<File> nbts = getAllNbts(dir, null);
nbts.parallelStream().forEach((file) -> {
DataFixerAPI.LOGGER.info("Loading NBT " + file);
try {
CompoundTag root = NbtIo.readCompressed(file);
boolean[] changed = {false};
if (root.contains("palette")){
ListTag items = root.getList("palette", Tag.TAG_COMPOUND);
items.forEach(inTag -> {
CompoundTag tag = (CompoundTag)inTag;
changed[0] |= profile.replaceStringFromIDs(tag, "Name");
});
}
if (changed[0]){
DataFixerAPI.LOGGER.info("Writing NBT " + file);
NbtIo.writeCompressed(root, file);
}
}
catch (IOException e) {
e.printStackTrace();
}
});
}
private static List<File> getAllNbts(File dir, List<File> list) {
if (list == null) {
list = new ArrayList<>();
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
getAllNbts(file, list);
} else if (file.isFile() && file.getName().endsWith(".nbt")) {
list.add(file);
}
}
return list;
}
final public CompoundTag getLevelDat(File levelBaseDir){
@ -124,12 +177,13 @@ public class MigrationProfile {
final public void markApplied() {
for (String modID : mods) {
DataFixerAPI.LOGGER.info("Updating Patch-Level for '{}' from {} to {}", modID, ModUtil.convertModVersion(currentPatchLevel(modID)), ModUtil.convertModVersion(Patch.maxPatchLevel(modID)));
config.putString(modID, Patch.maxPatchVersion(modID));
if (config!=null)
config.putString(modID, Patch.maxPatchVersion(modID));
}
}
public String currentPatchVersion(@NotNull String modID) {
if (!config.contains(modID)) return "0.0.0";
if (config==null || !config.contains(modID)) return "0.0.0";
return config.getString(modID);
}
@ -255,4 +309,12 @@ public class MigrationProfile {
}
}
}
public boolean patchBlockState(ListTag palette, ListTag states) throws PatchDidiFailException{
boolean changed = false;
for (PatchBiFunction<ListTag, ListTag, Boolean> f : statePatchers) {
changed |= f.apply(palette, states, this);
}
return changed;
}
}

View file

@ -1,6 +1,7 @@
package ru.bclib.api.datafixer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import org.jetbrains.annotations.NotNull;
import ru.bclib.util.ModUtil;
@ -30,6 +31,11 @@ public abstract class Patch {
@NotNull
public final String modID;
/**
* This Mod is tested for each level start
*/
public final boolean alwaysApply;
static List<Patch> getALL() {
return ALL;
}
@ -81,6 +87,19 @@ public abstract class Patch {
* {@link Patch#maxPatchVersion(String)}
*/
protected Patch(@NotNull String modID, String version) {
this(modID, version, false);
}
/**
* Internal Constructor used to create patches that can allways run (no matter what patchlevel a level has)
* @param modID The ID of the Mod
* @param version The mod-version that introduces the patch. When {@Code runAllways} is set, this version will
* determine the patchlevel that is written to the level
* @param alwaysApply When true, this patch is always active, no matter the patchlevel of the world.
* This should be used sparingly and just for patches that apply to level.dat (as they only take
* effect when changes are detected). Use {@link ForcedLevelPatch} to instatiate.
*/
Patch(@NotNull String modID, String version, boolean alwaysApply) {
//Patchlevels need to be unique and registered in ascending order
if (modID == null || "".equals(modID)) {
throw new RuntimeException("[INTERNAL ERROR] Patches need a valid modID!");
@ -91,6 +110,7 @@ public abstract class Patch {
}
this.version = version;
this.alwaysApply = alwaysApply;
this.level = ModUtil.convertModVersion(version);
if (!ALL.stream()
.filter(p -> p.modID
@ -145,22 +165,46 @@ public abstract class Patch {
*/
public PatchFunction<CompoundTag, Boolean> getWorldDataPatcher() { return null; }
/**
* Return a {@link PatchBiFunction} that is called with pallette and blockstate of
* each chunk in every region. This method is called AFTER all ID replacements
* from {@link #getIDReplacements()} were applied to the pallete.
*
* The first parameter is the palette and the second is the blockstate.
*
* The function needs to return {@code true}, if changes were made to the data.
* If an error occurs, the method should throw a {@link PatchDidiFailException}
*
* The default implementation of this method returns null.
*
* @return {@code true} if changes were applied and we need to save the data
*/
public PatchBiFunction<ListTag, ListTag, Boolean> getBlockStatePatcher() { return null; }
/**
* Generates ready to use data for all currently registered patches. The list of
* patches is selected by the current patch-level of the world.
* <p>
* A {@link #Patch} with a given {@link #level} is only included if the patch-level of the
* world is less
* @param config The current patch-level configuration
* @param levelBaseDir The location of the level
* @param config The current patch-level configuration*
* @return a new {@link MigrationProfile} Object.
*/
static MigrationProfile createMigrationData(CompoundTag config) {
return new MigrationProfile(config);
return new MigrationProfile(config, false);
}
/**
* This method is supposed to be used by developers to apply id-patches to custom nbt structures. It is only
* available in Developer-Mode
*
*/
static MigrationProfile createMigrationData() {
return new MigrationProfile(null, true);
}
/**
* Returns a list of paths,where your mod may IDs in your {@link ru.bclib.api.WorldDataAPI}-File.
* Returns a list of paths where your mod stores IDs in your {@link ru.bclib.api.WorldDataAPI}-File.
* <p>
* {@link DataFixerAPI} will use information from the latest patch that returns a non-null-result. This list is used
* to automatically fix changed IDs from all active patches (see {@link Patch#getIDReplacements()}

View file

@ -0,0 +1,6 @@
package ru.bclib.api.datafixer;
@FunctionalInterface
public interface PatchBiFunction<U, V, R> {
R apply(U t, V v, MigrationProfile profile) throws PatchDidiFailException;
}

View file

@ -25,7 +25,7 @@ import java.util.List;
import java.util.function.Consumer;
public class BaseLeavesBlock extends LeavesBlock implements BlockModelProvider, RenderLayerProvider {
private final Block sapling;
protected final Block sapling;
private static FabricBlockSettings makeLeaves(MaterialColor color) {
return FabricBlockSettings.copyOf(Blocks.OAK_LEAVES)
@ -37,12 +37,12 @@ public class BaseLeavesBlock extends LeavesBlock implements BlockModelProvider,
.suffocates((state, world, pos) -> false)
.blockVision((state, world, pos) -> false);
}
public BaseLeavesBlock(Block sapling, MaterialColor color, Consumer<FabricBlockSettings> customizeProperties) {
super(BaseBlock.acceptAndReturn(customizeProperties, makeLeaves(color)));
this.sapling = sapling;
}
public BaseLeavesBlock(Block sapling, MaterialColor color, int light, Consumer<FabricBlockSettings> customizeProperties) {
super(BaseBlock.acceptAndReturn(customizeProperties, makeLeaves(color).luminance(light)));
this.sapling = sapling;

View file

@ -9,6 +9,7 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.OreBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
@ -28,11 +29,16 @@ public class BaseOreBlock extends OreBlock implements BlockModelProvider {
private final int maxCount;
public BaseOreBlock(Item drop, int minCount, int maxCount, int experience) {
super(FabricBlockSettings.of(Material.STONE, MaterialColor.SAND)
this(drop, minCount, maxCount, experience, FabricBlockSettings.of(Material.STONE, MaterialColor.SAND)
.hardness(3F)
.resistance(9F)
.requiresCorrectToolForDrops()
.sound(SoundType.STONE), UniformInt.of(1, experience));
.sound(SoundType.STONE));
}
public BaseOreBlock(Item drop, int minCount, int maxCount, int experience, Properties properties) {
super(properties, UniformInt.of(experience>0?1:0, experience));
this.dropItem = drop;
this.minCount = minCount;
this.maxCount = maxCount;
@ -41,10 +47,14 @@ public class BaseOreBlock extends OreBlock implements BlockModelProvider {
@Override
@SuppressWarnings("deprecation")
public List<ItemStack> getDrops(BlockState state, LootContext.Builder builder) {
return getDroppedItems(this, dropItem, maxCount, minCount, state, builder);
}
public static List<ItemStack> getDroppedItems(ItemLike block, Item dropItem, int maxCount, int minCount, BlockState state, LootContext.Builder builder) {
ItemStack tool = builder.getParameter(LootContextParams.TOOL);
if (tool != null && tool.isCorrectToolForDrops(state)) {
if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) {
return Collections.singletonList(new ItemStack(this));
return Collections.singletonList(new ItemStack(block));
}
int count;
int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool);

View file

@ -0,0 +1,32 @@
package ru.bclib.blocks;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public abstract class FeatureHangingSaplingBlock extends FeatureSaplingBlockCommon{
private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12);
public FeatureHangingSaplingBlock() {
super();
}
public FeatureHangingSaplingBlock(int light) {
super(light);
}
@Override
public boolean canSurvive(BlockState blockState, LevelReader levelReader, BlockPos blockPos) {
final BlockPos target = blockPos.above();
return this.mayPlaceOn(levelReader.getBlockState(target), levelReader, target);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) {
return SHAPE;
}
}

View file

@ -1,134 +1,26 @@
package ru.bclib.blocks;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SaplingBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import ru.bclib.client.models.BasePatterns;
import ru.bclib.client.models.ModelsHelper;
import ru.bclib.client.models.PatternsHelper;
import ru.bclib.client.render.BCLRenderLayer;
import ru.bclib.interfaces.BlockModelProvider;
import ru.bclib.interfaces.RenderLayerProvider;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Random;
@SuppressWarnings("deprecation")
public abstract class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProvider, BlockModelProvider {
public abstract class FeatureSaplingBlock extends FeatureSaplingBlockCommon {
private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12);
public FeatureSaplingBlock() {
super(
null,
FabricBlockSettings.of(Material.PLANT)
.breakByHand(true)
.collidable(false)
.instabreak()
.sound(SoundType.GRASS)
.randomTicks()
);
super();
}
public FeatureSaplingBlock(int light) {
super(
null,
FabricBlockSettings.of(Material.PLANT)
.breakByHand(true)
.collidable(false)
.luminance(light)
.instabreak()
.sound(SoundType.GRASS)
.randomTicks()
);
}
protected abstract Feature<?> getFeature();
@Override
public List<ItemStack> getDrops(BlockState state, LootContext.Builder builder) {
return Collections.singletonList(new ItemStack(this));
super(light);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) {
return SHAPE;
}
@Override
public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
if (!canSurvive(state, world, pos)) return Blocks.AIR.defaultBlockState();
else return state;
}
@Override
public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) {
return random.nextInt(16) == 0;
}
@Override
public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, Random random) {
FeaturePlaceContext context = new FeaturePlaceContext(
Optional.empty(),
world,
world.getChunkSource().getGenerator(),
random,
pos,
null
);
getFeature().place(context);
}
@Override
public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) {
this.tick(state, world, pos, random);
}
@Override
public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) {
super.tick(state, world, pos, random);
if (isBonemealSuccess(world, random, pos, state)) {
performBonemeal(world, random, pos, state);
}
}
@Override
public BCLRenderLayer getRenderLayer() {
return BCLRenderLayer.CUTOUT;
}
@Override
@Environment(EnvType.CLIENT)
public BlockModel getItemModel(ResourceLocation resourceLocation) {
return ModelsHelper.createBlockItem(resourceLocation);
}
@Override
@Environment(EnvType.CLIENT)
public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) {
Optional<String> pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS, resourceLocation);
return ModelsHelper.fromPattern(pattern);
}
}

View file

@ -0,0 +1,129 @@
package ru.bclib.blocks;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SaplingBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.storage.loot.LootContext;
import org.jetbrains.annotations.Nullable;
import ru.bclib.client.models.BasePatterns;
import ru.bclib.client.models.ModelsHelper;
import ru.bclib.client.models.PatternsHelper;
import ru.bclib.client.render.BCLRenderLayer;
import ru.bclib.interfaces.BlockModelProvider;
import ru.bclib.interfaces.RenderLayerProvider;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Random;
abstract class FeatureSaplingBlockCommon extends SaplingBlock implements RenderLayerProvider, BlockModelProvider {
public FeatureSaplingBlockCommon() {
super(
null,
FabricBlockSettings.of(Material.PLANT)
.breakByHand(true)
.collidable(false)
.instabreak()
.sound(SoundType.GRASS)
.randomTicks()
);
}
public FeatureSaplingBlockCommon(int light) {
super(
null,
FabricBlockSettings.of(Material.PLANT)
.breakByHand(true)
.collidable(false)
.luminance(light)
.instabreak()
.sound(SoundType.GRASS)
.randomTicks()
);
}
@Deprecated
/**
* Override {@link #getFeature(BlockState)} directly. Will be removed in 5.x
*/
protected Feature<?> getFeature() { return null; }
protected Feature<?> getFeature(BlockState state){
return getFeature();
}
@Override
public List<ItemStack> getDrops(BlockState state, LootContext.Builder builder) {
return Collections.singletonList(new ItemStack(this));
}
@Override
public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
if (!canSurvive(state, world, pos)) return Blocks.AIR.defaultBlockState();
else return state;
}
@Override
public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) {
return random.nextInt(16) == 0;
}
@Override
public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, Random random) {
FeaturePlaceContext context = new FeaturePlaceContext(
world,
world.getChunkSource().getGenerator(),
random,
pos,
null
);
getFeature(blockState).place(context);
}
@Override
public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) {
this.tick(state, world, pos, random);
}
@Override
public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) {
super.tick(state, world, pos, random);
if (isBonemealSuccess(world, random, pos, state)) {
performBonemeal(world, random, pos, state);
}
}
@Override
public BCLRenderLayer getRenderLayer() {
return BCLRenderLayer.CUTOUT;
}
@Override
@Environment(EnvType.CLIENT)
public BlockModel getItemModel(ResourceLocation resourceLocation) {
return ModelsHelper.createBlockItem(resourceLocation);
}
@Override
@Environment(EnvType.CLIENT)
public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) {
Optional<String> pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS, resourceLocation);
return ModelsHelper.fromPattern(pattern);
}
}

View file

@ -0,0 +1,16 @@
package ru.bclib.blocks;
import net.minecraft.world.level.material.MaterialColor;
public class LeveledAnvilBlock extends BaseAnvilBlock{
protected final int level;
public LeveledAnvilBlock(MaterialColor color, int level) {
super(color);
this.level = level;
}
public int getCraftingLevel() {
return level;
}
}

View file

@ -4,6 +4,7 @@ import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.material.MaterialColor;
import ru.bclib.api.TagAPI;
import ru.bclib.client.render.BCLRenderLayer;
import ru.bclib.interfaces.RenderLayerProvider;
@ -17,6 +18,8 @@ public class SimpleLeavesBlock extends BaseBlockNotFull implements RenderLayerPr
.isValidSpawn((state, world, pos, type) -> false)
.isSuffocating((state, world, pos) -> false)
.isViewBlocking((state, world, pos) -> false));
TagAPI.addTags(this, TagAPI.BLOCK_LEAVES);
}
public SimpleLeavesBlock(MaterialColor color, int light) {
@ -29,6 +32,8 @@ public class SimpleLeavesBlock extends BaseBlockNotFull implements RenderLayerPr
.isValidSpawn((state, world, pos, type) -> false)
.isSuffocating((state, world, pos) -> false)
.isViewBlocking((state, world, pos) -> false));
TagAPI.addTags(this, TagAPI.BLOCK_LEAVES);
}
@Override

View file

@ -88,18 +88,21 @@ public class ModelsHelper {
public static class MultiPartBuilder {
private final static MultiPartBuilder BUILDER = new MultiPartBuilder();
//private final static MultiPartBuilder BUILDER = new MultiPartBuilder();
public static MultiPartBuilder create(StateDefinition<Block, BlockState> stateDefinition) {
BUILDER.stateDefinition = stateDefinition;
BUILDER.modelParts.clear();
return BUILDER;
// BUILDER.stateDefinition = stateDefinition;
//BUILDER.modelParts.clear();
// return BUILDER;
return new MultiPartBuilder(stateDefinition);
}
private final List<ModelPart> modelParts = Lists.newArrayList();
private StateDefinition<Block, BlockState> stateDefinition;
private MultiPartBuilder() {}
private MultiPartBuilder(StateDefinition<Block, BlockState> stateDefinition) {
this.stateDefinition = stateDefinition;
}
public ModelPart part(ResourceLocation modelId) {
ModelPart part = new ModelPart(modelId);

View file

@ -3,6 +3,7 @@ package ru.bclib.client.render;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffectInstance;
@ -22,46 +23,77 @@ public class CustomBackgroundRenderer {
private static final MutableBlockPos MUT_POS = new MutableBlockPos();
private static final float[] FOG_DENSITY = new float[8];
private static final int GRID_SIZE = 32;
private static float fogStart = 0;
private static float fogEnd = 192;
public static boolean applyFogDensity(Camera camera, FogRenderer.FogMode fogMode, float viewDistance, boolean thickFog) {
Entity entity = camera.getEntity();
FogType fogType = camera.getFluidInCamera();
if (fogType != FogType.WATER) {
if (shouldIgnore(entity.level, (int) entity.getX(), (int) entity.getEyeY(), (int) entity.getZ())) {
return false;
}
float fog = getFogDensity(entity.level, entity.getX(), entity.getEyeY(), entity.getZ());
BackgroundInfo.fogDensity = fog;
float start = viewDistance * 0.75F / fog;
float end = viewDistance / fog;
if (entity instanceof LivingEntity) {
LivingEntity livingEntity = (LivingEntity) entity;
MobEffectInstance effect = livingEntity.getEffect(MobEffects.BLINDNESS);
if (effect != null) {
int duration = effect.getDuration();
if (duration > 20) {
start = 0;
end *= 0.03F;
BackgroundInfo.blindness = 1;
}
else {
float delta = (float) duration / 20F;
BackgroundInfo.blindness = delta;
start = Mth.lerp(delta, start, 0);
end = Mth.lerp(delta, end, end * 0.03F);
}
if (fogType == FogType.WATER || fogType == FogType.LAVA || fogMode != FogMode.FOG_SKY) {
BackgroundInfo.fogDensity = 1;
return false;
}
Entity entity = camera.getEntity();
if (!isForcedDimension(entity.level) && shouldIgnoreArea(entity.level, (int) entity.getX(), (int) entity.getEyeY(), (int) entity.getZ())) {
BackgroundInfo.fogDensity = 1;
return false;
}
float fog = getFogDensity(entity.level, entity.getX(), entity.getEyeY(), entity.getZ());
BackgroundInfo.fogDensity = fog;
if (thickFog) {
fogStart = viewDistance * 0.05F / fog;
fogEnd = Math.min(viewDistance, 192.0F) * 0.5F / fog;
}
else {
fogStart = viewDistance * 0.75F / fog;
fogEnd = viewDistance / fog;
}
if (entity instanceof LivingEntity) {
LivingEntity livingEntity = (LivingEntity) entity;
MobEffectInstance effect = livingEntity.getEffect(MobEffects.BLINDNESS);
if (effect != null) {
int duration = effect.getDuration();
if (duration > 20) {
fogStart = 0;
fogEnd *= 0.03F;
BackgroundInfo.blindness = 1;
}
else {
BackgroundInfo.blindness = 0;
float delta = (float) duration / 20F;
BackgroundInfo.blindness = delta;
fogStart = Mth.lerp(delta, fogStart, 0);
fogEnd = Mth.lerp(delta, fogEnd, fogEnd * 0.03F);
}
}
RenderSystem.setShaderFogStart(start);
RenderSystem.setShaderFogEnd(end);
return true;
else {
BackgroundInfo.blindness = 0;
}
}
return false;
RenderSystem.setShaderFogStart(fogStart);
RenderSystem.setShaderFogEnd(fogEnd);
return true;
}
private static boolean isForcedDimension(Level level) {
return level.dimension() == Level.END || level.dimension() == Level.NETHER;
}
private static boolean shouldIgnoreArea(Level level, int x, int y, int z) {
for (int i = -8; i <= 8; i += 8) {
for (int j = -8; j <= 8; j += 8) {
if (!shouldIgnore(level, x + i, y, z + j)) {
return false;
}
}
}
return true;
}
private static boolean shouldIgnore(Level level, int x, int y, int z) {

View file

@ -39,358 +39,372 @@ import ru.bclib.complexmaterials.entry.RecipeEntry;
import ru.bclib.recipes.GridRecipe;
public class WoodenComplexMaterial extends ComplexMaterial {
public static final ResourceLocation MATERIAL_ID = BCLib.makeID("wooden_material");
public static final String BLOCK_CRAFTING_TABLE = "crafting_table";
public static final String BLOCK_STRIPPED_BARK = "stripped_bark";
public static final String BLOCK_STRIPPED_LOG = "stripped_log";
public static final String BLOCK_PRESSURE_PLATE = "plate";
public static final String BLOCK_BOOKSHELF = "bookshelf";
public static final String BLOCK_COMPOSTER = "composter";
public static final String BLOCK_TRAPDOOR = "trapdoor";
public static final String BLOCK_BARREL = "barrel";
public static final String BLOCK_BUTTON = "button";
public static final String BLOCK_LADDER = "ladder";
public static final String BLOCK_PLANKS = "planks";
public static final String BLOCK_STAIRS = "stairs";
public static final String BLOCK_CHEST = "chest";
public static final String BLOCK_FENCE = "fence";
public static final String BLOCK_BARK = "bark";
public static final String BLOCK_DOOR = "door";
public static final String BLOCK_GATE = "gate";
public static final String BLOCK_SIGN = "sign";
public static final String BLOCK_SLAB = "slab";
public static final String BLOCK_LOG = "log";
public static final String TAG_LOGS = "logs";
public final MaterialColor planksColor;
public final MaterialColor woodColor;
public WoodenComplexMaterial(String modID, String baseName, String receipGroupPrefix, MaterialColor woodColor, MaterialColor planksColor) {
super(modID, baseName, receipGroupPrefix);
this.planksColor = planksColor;
this.woodColor = woodColor;
}
@Override
protected FabricBlockSettings getBlockSettings() {
return FabricBlockSettings.copyOf(Blocks.OAK_PLANKS).mapColor(planksColor);
}
@Override
public ResourceLocation getMaterialID() {
return MATERIAL_ID;
}
@Override
protected void initTags() {
addBlockTag(TagAPI.makeBlockTag(getModID(), getBaseName() + "_logs"));
addItemTag(TagAPI.makeItemTag(getModID(), getBaseName() + "_logs"));
}
@Override
protected void initDefault(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) {
initBase(blockSettings, itemSettings);
initStorage(blockSettings, itemSettings);
initDecorations(blockSettings, itemSettings);
}
final protected void initBase(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) {
Tag.Named<Block> tagBlockLog = getBlockTag(TAG_LOGS);
Tag.Named<Item> tagItemLog = getItemTag(TAG_LOGS);
addBlockEntry(
new BlockEntry(BLOCK_STRIPPED_LOG, (complexMaterial, settings) -> {
return new BaseRotatedPillarBlock(settings);
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(
new BlockEntry(BLOCK_STRIPPED_BARK, (complexMaterial, settings) -> {
return new BaseBarkBlock(settings);
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(
new BlockEntry(BLOCK_LOG, (complexMaterial, settings) -> {
return new BaseStripableLogBlock(woodColor, getBlock(BLOCK_STRIPPED_LOG));
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(
new BlockEntry(BLOCK_BARK, (complexMaterial, settings) -> {
return new StripableBarkBlock(woodColor, getBlock(BLOCK_STRIPPED_BARK));
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(new BlockEntry(BLOCK_PLANKS, (complexMaterial, settings) -> {
return new BaseBlock(settings);
}).setBlockTags(BlockTags.PLANKS).setItemTags(ItemTags.PLANKS));
addBlockEntry(new BlockEntry(BLOCK_STAIRS, (complexMaterial, settings) -> {
return new BaseStairsBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.WOODEN_STAIRS, BlockTags.STAIRS).setItemTags(ItemTags.WOODEN_STAIRS, ItemTags.STAIRS));
addBlockEntry(new BlockEntry(BLOCK_SLAB, (complexMaterial, settings) -> {
return new BaseSlabBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.WOODEN_SLABS, BlockTags.SLABS).setItemTags(ItemTags.WOODEN_SLABS, ItemTags.SLABS));
addBlockEntry(new BlockEntry(BLOCK_FENCE, (complexMaterial, settings) -> {
return new BaseFenceBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.FENCES, BlockTags.WOODEN_FENCES).setItemTags(ItemTags.FENCES, ItemTags.WOODEN_FENCES));
addBlockEntry(new BlockEntry(BLOCK_GATE, (complexMaterial, settings) -> {
return new BaseGateBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.FENCE_GATES));
addBlockEntry(new BlockEntry(BLOCK_BUTTON, (complexMaterial, settings) -> {
return new BaseWoodenButtonBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.BUTTONS, BlockTags.WOODEN_BUTTONS).setItemTags(ItemTags.BUTTONS, ItemTags.WOODEN_BUTTONS));
addBlockEntry(new BlockEntry(BLOCK_PRESSURE_PLATE, (complexMaterial, settings) -> {
return new WoodenPressurePlateBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.PRESSURE_PLATES, BlockTags.WOODEN_PRESSURE_PLATES).setItemTags(ItemTags.WOODEN_PRESSURE_PLATES));
addBlockEntry(new BlockEntry(BLOCK_TRAPDOOR, (complexMaterial, settings) -> {
return new BaseTrapdoorBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.TRAPDOORS, BlockTags.WOODEN_TRAPDOORS).setItemTags(ItemTags.TRAPDOORS, ItemTags.WOODEN_TRAPDOORS));
addBlockEntry(new BlockEntry(BLOCK_DOOR, (complexMaterial, settings) -> {
return new BaseDoorBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.DOORS, BlockTags.WOODEN_DOORS).setItemTags(ItemTags.DOORS, ItemTags.WOODEN_DOORS));
addBlockEntry(new BlockEntry(BLOCK_LADDER, (complexMaterial, settings) -> {
return new BaseLadderBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.CLIMBABLE));
addBlockEntry(new BlockEntry(BLOCK_SIGN, (complexMaterial, settings) -> {
return new BaseSignBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.SIGNS).setItemTags(ItemTags.SIGNS));
}
final protected void initStorage(FabricBlockSettings blockSettings, FabricItemSettings itemSettings){
addBlockEntry(new BlockEntry(BLOCK_CHEST, (complexMaterial, settings) -> {
return new BaseChestBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_CHEST).setItemTags(TagAPI.ITEM_CHEST));
addBlockEntry(new BlockEntry(BLOCK_BARREL, (complexMaterial, settings) -> {
return new BaseBarrelBlock(getBlock(BLOCK_PLANKS));
}));
}
final protected void initDecorations(FabricBlockSettings blockSettings, FabricItemSettings itemSettings){
addBlockEntry(new BlockEntry(BLOCK_CRAFTING_TABLE, (complexMaterial, settings) -> {
return new BaseCraftingTableBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_WORKBENCHES).setItemTags(TagAPI.ITEM_WORKBENCHES));
addBlockEntry(new BlockEntry(BLOCK_BOOKSHELF, (complexMaterial, settings) -> {
return new BaseBookshelfBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_BOOKSHELVES));
addBlockEntry(new BlockEntry(BLOCK_COMPOSTER, (complexMaterial, settings) -> {
return new BaseComposterBlock(getBlock(BLOCK_PLANKS));
}));
}
@Override
protected void initFlammable(FlammableBlockRegistry registry) {
getBlocks().forEach(block -> {
registry.add(block, 5, 20);
});
registry.add(getBlock(BLOCK_LOG), 5, 5);
registry.add(getBlock(BLOCK_BARK), 5, 5);
registry.add(getBlock(BLOCK_STRIPPED_LOG), 5, 5);
registry.add(getBlock(BLOCK_STRIPPED_BARK), 5, 5);
}
@Override
public void initDefaultRecipes() {
Block planks = getBlock(BLOCK_PLANKS);
addRecipeEntry(new RecipeEntry("planks", (material, config, id) -> {
Block log_stripped = getBlock(BLOCK_STRIPPED_LOG);
Block bark_stripped = getBlock(BLOCK_STRIPPED_BARK);
Block log = getBlock(BLOCK_LOG);
Block bark = getBlock(BLOCK_BARK);
GridRecipe.make(id, planks)
.checkConfig(config)
.setOutputCount(4)
.setList("#")
.addMaterial('#', log, bark, log_stripped, bark_stripped)
.setGroup(receipGroupPrefix +"_planks")
.build();
}));
addRecipeEntry(new RecipeEntry("stairs", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_STAIRS))
.checkConfig(config)
.setOutputCount(4)
.setShape("# ", "## ", "###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_planks_stairs")
.build();
}));
addRecipeEntry(new RecipeEntry("slab", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_SLAB))
.checkConfig(config)
.setOutputCount(6)
.setShape("###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_planks_slabs")
.build();
}));
addRecipeEntry(new RecipeEntry("fence", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_FENCE))
.checkConfig(config)
.setOutputCount(3)
.setShape("#I#", "#I#")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix +"_planks_fences")
.build();
}));
addRecipeEntry(new RecipeEntry("gate", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_GATE))
.checkConfig(config)
.setShape("I#I", "I#I")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix +"_planks_gates")
.build();
}));
addRecipeEntry(new RecipeEntry("button", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BUTTON))
.checkConfig(config)
.setList("#")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_planks_buttons")
.build();
}));
addRecipeEntry(new RecipeEntry("pressure_plate", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_PRESSURE_PLATE))
.checkConfig(config)
.setShape("##")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_planks_plates")
.build();
}));
addRecipeEntry(new RecipeEntry("trapdoor", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_TRAPDOOR))
.checkConfig(config)
.setOutputCount(2)
.setShape("###", "###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_trapdoors")
.build();
}));
addRecipeEntry(new RecipeEntry("door", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_DOOR))
.checkConfig(config)
.setOutputCount(3)
.setShape("##", "##", "##")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_doors")
.build();
}));
addRecipeEntry(new RecipeEntry("crafting_table", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_CRAFTING_TABLE))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_tables")
.build();
}));
addRecipeEntry(new RecipeEntry("ladder", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_LADDER))
.checkConfig(config)
.setOutputCount(3)
.setShape("I I", "I#I", "I I")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix +"_ladders")
.build();
}));
addRecipeEntry(new RecipeEntry("sign", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_SIGN))
.checkConfig(config)
.setOutputCount(3)
.setShape("###", "###", " I ")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix +"_signs")
.build();
}));
addRecipeEntry(new RecipeEntry("chest", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_CHEST))
.checkConfig(config)
.setShape("###", "# #", "###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix +"_chests")
.build();
}));
addRecipeEntry(new RecipeEntry("barrel", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BARREL))
.checkConfig(config)
.setShape("#S#", "# #", "#S#")
.addMaterial('#', planks)
.addMaterial('S', getBlock(BLOCK_SLAB))
.setGroup(receipGroupPrefix +"_barrels")
.build();
}));
addRecipeEntry(new RecipeEntry("bookshelf", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BOOKSHELF))
.checkConfig(config)
.setShape("###", "PPP", "###")
.addMaterial('#', planks)
.addMaterial('P', Items.BOOK)
.setGroup(receipGroupPrefix +"_bookshelves")
.build();
}));
addRecipeEntry(new RecipeEntry("bark", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BARK))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_LOG))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("log", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_LOG))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_BARK))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("stripped_bark", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_STRIPPED_BARK))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_STRIPPED_LOG))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("stripped_log", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_STRIPPED_LOG))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_STRIPPED_BARK))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("composter", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_COMPOSTER))
.checkConfig(config)
.setShape("# #", "# #", "###")
.addMaterial('#', getBlock(BLOCK_SLAB))
.build();
}));
addRecipeEntry(new RecipeEntry("shulker", (material, config, id) -> {
GridRecipe.make(id, Blocks.SHULKER_BOX)
.checkConfig(config)
.setShape("S", "#", "S")
.addMaterial('S', Items.SHULKER_SHELL)
.addMaterial('#', getBlock(BLOCK_CHEST))
.build();
}));
}
public static final ResourceLocation MATERIAL_ID = BCLib.makeID("wooden_material");
public static final String BLOCK_CRAFTING_TABLE = "crafting_table";
public static final String BLOCK_STRIPPED_BARK = "stripped_bark";
public static final String BLOCK_STRIPPED_LOG = "stripped_log";
public static final String BLOCK_PRESSURE_PLATE = "plate";
public static final String BLOCK_BOOKSHELF = "bookshelf";
public static final String BLOCK_COMPOSTER = "composter";
public static final String BLOCK_TRAPDOOR = "trapdoor";
public static final String BLOCK_BARREL = "barrel";
public static final String BLOCK_BUTTON = "button";
public static final String BLOCK_LADDER = "ladder";
public static final String BLOCK_PLANKS = "planks";
public static final String BLOCK_STAIRS = "stairs";
public static final String BLOCK_CHEST = "chest";
public static final String BLOCK_FENCE = "fence";
public static final String BLOCK_BARK = "bark";
public static final String BLOCK_DOOR = "door";
public static final String BLOCK_GATE = "gate";
public static final String BLOCK_SIGN = "sign";
public static final String BLOCK_SLAB = "slab";
public static final String BLOCK_LOG = "log";
public static final String TAG_LOGS = "logs";
public final MaterialColor planksColor;
public final MaterialColor woodColor;
public WoodenComplexMaterial(String modID, String baseName, String receipGroupPrefix, MaterialColor woodColor, MaterialColor planksColor) {
super(modID, baseName, receipGroupPrefix);
this.planksColor = planksColor;
this.woodColor = woodColor;
}
@Override
protected FabricBlockSettings getBlockSettings() {
return FabricBlockSettings.copyOf(Blocks.OAK_PLANKS)
.materialColor(planksColor);
}
@Override
public ResourceLocation getMaterialID() {
return MATERIAL_ID;
}
@Override
protected void initTags() {
addBlockTag(TagAPI.makeBlockTag(getModID(), getBaseName() + "_logs"));
addItemTag(TagAPI.makeItemTag(getModID(), getBaseName() + "_logs"));
}
@Override
protected void initDefault(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) {
initBase(blockSettings, itemSettings);
initStorage(blockSettings, itemSettings);
initDecorations(blockSettings, itemSettings);
}
final protected void initBase(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) {
Tag.Named<Block> tagBlockLog = getBlockTag(TAG_LOGS);
Tag.Named<Item> tagItemLog = getItemTag(TAG_LOGS);
addBlockEntry(
new BlockEntry(BLOCK_STRIPPED_LOG, (complexMaterial, settings) -> {
return new BaseRotatedPillarBlock(settings);
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(
new BlockEntry(BLOCK_STRIPPED_BARK, (complexMaterial, settings) -> {
return new BaseBarkBlock(settings);
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(
new BlockEntry(BLOCK_LOG, (complexMaterial, settings) -> {
return new BaseStripableLogBlock(woodColor, getBlock(BLOCK_STRIPPED_LOG));
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(
new BlockEntry(BLOCK_BARK, (complexMaterial, settings) -> {
return new StripableBarkBlock(woodColor, getBlock(BLOCK_STRIPPED_BARK));
})
.setBlockTags(BlockTags.LOGS, BlockTags.LOGS_THAT_BURN, tagBlockLog)
.setItemTags(ItemTags.LOGS, ItemTags.LOGS_THAT_BURN, tagItemLog)
);
addBlockEntry(new BlockEntry(BLOCK_PLANKS, (complexMaterial, settings) -> {
return new BaseBlock(settings);
}).setBlockTags(BlockTags.PLANKS)
.setItemTags(ItemTags.PLANKS));
addBlockEntry(new BlockEntry(BLOCK_STAIRS, (complexMaterial, settings) -> {
return new BaseStairsBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.WOODEN_STAIRS, BlockTags.STAIRS)
.setItemTags(ItemTags.WOODEN_STAIRS, ItemTags.STAIRS));
addBlockEntry(new BlockEntry(BLOCK_SLAB, (complexMaterial, settings) -> {
return new BaseSlabBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.WOODEN_SLABS, BlockTags.SLABS)
.setItemTags(ItemTags.WOODEN_SLABS, ItemTags.SLABS));
addBlockEntry(new BlockEntry(BLOCK_FENCE, (complexMaterial, settings) -> {
return new BaseFenceBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.FENCES, BlockTags.WOODEN_FENCES)
.setItemTags(ItemTags.FENCES, ItemTags.WOODEN_FENCES));
addBlockEntry(new BlockEntry(BLOCK_GATE, (complexMaterial, settings) -> {
return new BaseGateBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.FENCE_GATES));
addBlockEntry(new BlockEntry(BLOCK_BUTTON, (complexMaterial, settings) -> {
return new BaseWoodenButtonBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.BUTTONS, BlockTags.WOODEN_BUTTONS)
.setItemTags(ItemTags.BUTTONS, ItemTags.WOODEN_BUTTONS));
addBlockEntry(new BlockEntry(BLOCK_PRESSURE_PLATE, (complexMaterial, settings) -> {
return new WoodenPressurePlateBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.PRESSURE_PLATES, BlockTags.WOODEN_PRESSURE_PLATES)
.setItemTags(ItemTags.WOODEN_PRESSURE_PLATES));
addBlockEntry(new BlockEntry(BLOCK_TRAPDOOR, (complexMaterial, settings) -> {
return new BaseTrapdoorBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.TRAPDOORS, BlockTags.WOODEN_TRAPDOORS)
.setItemTags(ItemTags.TRAPDOORS, ItemTags.WOODEN_TRAPDOORS));
addBlockEntry(new BlockEntry(BLOCK_DOOR, (complexMaterial, settings) -> {
return new BaseDoorBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.DOORS, BlockTags.WOODEN_DOORS)
.setItemTags(ItemTags.DOORS, ItemTags.WOODEN_DOORS));
addBlockEntry(new BlockEntry(BLOCK_LADDER, (complexMaterial, settings) -> {
return new BaseLadderBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.CLIMBABLE));
addBlockEntry(new BlockEntry(BLOCK_SIGN, (complexMaterial, settings) -> {
return new BaseSignBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(BlockTags.SIGNS)
.setItemTags(ItemTags.SIGNS));
}
final protected void initStorage(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) {
addBlockEntry(new BlockEntry(BLOCK_CHEST, (complexMaterial, settings) -> {
return new BaseChestBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_CHEST, TagAPI.BLOCK_WOODEN_CHEST)
.setItemTags(TagAPI.ITEM_CHEST, TagAPI.ITEM_WOODEN_CHEST));
addBlockEntry(new BlockEntry(BLOCK_BARREL, (complexMaterial, settings) -> {
return new BaseBarrelBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_BARREL, TagAPI.BLOCK_WOODEN_BARREL)
.setItemTags(TagAPI.ITEM_BARREL, TagAPI.ITEM_WOODEN_BARREL));
}
final protected void initDecorations(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) {
addBlockEntry(new BlockEntry(BLOCK_CRAFTING_TABLE, (complexMaterial, settings) -> {
return new BaseCraftingTableBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_WORKBENCHES)
.setItemTags(TagAPI.ITEM_WORKBENCHES));
addBlockEntry(new BlockEntry(BLOCK_BOOKSHELF, (complexMaterial, settings) -> {
return new BaseBookshelfBlock(getBlock(BLOCK_PLANKS));
}).setBlockTags(TagAPI.BLOCK_BOOKSHELVES));
addBlockEntry(new BlockEntry(BLOCK_COMPOSTER, (complexMaterial, settings) -> {
return new BaseComposterBlock(getBlock(BLOCK_PLANKS));
}));
}
@Override
protected void initFlammable(FlammableBlockRegistry registry) {
getBlocks().forEach(block -> {
registry.add(block, 5, 20);
});
registry.add(getBlock(BLOCK_LOG), 5, 5);
registry.add(getBlock(BLOCK_BARK), 5, 5);
registry.add(getBlock(BLOCK_STRIPPED_LOG), 5, 5);
registry.add(getBlock(BLOCK_STRIPPED_BARK), 5, 5);
}
@Override
public void initDefaultRecipes() {
Block planks = getBlock(BLOCK_PLANKS);
addRecipeEntry(new RecipeEntry("planks", (material, config, id) -> {
Block log_stripped = getBlock(BLOCK_STRIPPED_LOG);
Block bark_stripped = getBlock(BLOCK_STRIPPED_BARK);
Block log = getBlock(BLOCK_LOG);
Block bark = getBlock(BLOCK_BARK);
GridRecipe.make(id, planks)
.checkConfig(config)
.setOutputCount(4)
.setList("#")
.addMaterial('#', log, bark, log_stripped, bark_stripped)
.setGroup(receipGroupPrefix + "_planks")
.build();
}));
addRecipeEntry(new RecipeEntry("stairs", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_STAIRS))
.checkConfig(config)
.setOutputCount(4)
.setShape("# ", "## ", "###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_planks_stairs")
.build();
}));
addRecipeEntry(new RecipeEntry("slab", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_SLAB))
.checkConfig(config)
.setOutputCount(6)
.setShape("###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_planks_slabs")
.build();
}));
addRecipeEntry(new RecipeEntry("fence", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_FENCE))
.checkConfig(config)
.setOutputCount(3)
.setShape("#I#", "#I#")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix + "_planks_fences")
.build();
}));
addRecipeEntry(new RecipeEntry("gate", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_GATE))
.checkConfig(config)
.setShape("I#I", "I#I")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix + "_planks_gates")
.build();
}));
addRecipeEntry(new RecipeEntry("button", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BUTTON))
.checkConfig(config)
.setList("#")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_planks_buttons")
.build();
}));
addRecipeEntry(new RecipeEntry("pressure_plate", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_PRESSURE_PLATE))
.checkConfig(config)
.setShape("##")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_planks_plates")
.build();
}));
addRecipeEntry(new RecipeEntry("trapdoor", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_TRAPDOOR))
.checkConfig(config)
.setOutputCount(2)
.setShape("###", "###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_trapdoors")
.build();
}));
addRecipeEntry(new RecipeEntry("door", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_DOOR))
.checkConfig(config)
.setOutputCount(3)
.setShape("##", "##", "##")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_doors")
.build();
}));
addRecipeEntry(new RecipeEntry("crafting_table", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_CRAFTING_TABLE))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_tables")
.build();
}));
addRecipeEntry(new RecipeEntry("ladder", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_LADDER))
.checkConfig(config)
.setOutputCount(3)
.setShape("I I", "I#I", "I I")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix + "_ladders")
.build();
}));
addRecipeEntry(new RecipeEntry("sign", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_SIGN))
.checkConfig(config)
.setOutputCount(3)
.setShape("###", "###", " I ")
.addMaterial('#', planks)
.addMaterial('I', Items.STICK)
.setGroup(receipGroupPrefix + "_signs")
.build();
}));
addRecipeEntry(new RecipeEntry("chest", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_CHEST))
.checkConfig(config)
.setShape("###", "# #", "###")
.addMaterial('#', planks)
.setGroup(receipGroupPrefix + "_chests")
.build();
}));
addRecipeEntry(new RecipeEntry("barrel", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BARREL))
.checkConfig(config)
.setShape("#S#", "# #", "#S#")
.addMaterial('#', planks)
.addMaterial('S', getBlock(BLOCK_SLAB))
.setGroup(receipGroupPrefix + "_barrels")
.build();
}));
addRecipeEntry(new RecipeEntry("bookshelf", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BOOKSHELF))
.checkConfig(config)
.setShape("###", "PPP", "###")
.addMaterial('#', planks)
.addMaterial('P', Items.BOOK)
.setGroup(receipGroupPrefix + "_bookshelves")
.build();
}));
addRecipeEntry(new RecipeEntry("bark", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_BARK))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_LOG))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("log", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_LOG))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_BARK))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("stripped_bark", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_STRIPPED_BARK))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_STRIPPED_LOG))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("stripped_log", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_STRIPPED_LOG))
.checkConfig(config)
.setShape("##", "##")
.addMaterial('#', getBlock(BLOCK_STRIPPED_BARK))
.setOutputCount(3)
.build();
}));
addRecipeEntry(new RecipeEntry("composter", (material, config, id) -> {
GridRecipe.make(id, getBlock(BLOCK_COMPOSTER))
.checkConfig(config)
.setShape("# #", "# #", "###")
.addMaterial('#', getBlock(BLOCK_SLAB))
.build();
}));
addRecipeEntry(new RecipeEntry("shulker", (material, config, id) -> {
GridRecipe.make(id, Blocks.SHULKER_BOX)
.checkConfig(config)
.setShape("S", "#", "S")
.addMaterial('S', Items.SHULKER_SHELL)
.addMaterial('#', getBlock(BLOCK_CHEST))
.build();
}));
}
}

View file

@ -3,7 +3,7 @@ package ru.bclib.complexmaterials.entry;
import net.minecraft.resources.ResourceLocation;
import ru.bclib.complexmaterials.ComplexMaterial;
import ru.bclib.config.PathConfig;
import ru.bclib.util.TriConsumer;
import ru.bclib.interfaces.TriConsumer;
public class RecipeEntry extends ComplexMaterialEntry {
final TriConsumer<ComplexMaterial, PathConfig, ResourceLocation> initFunction;

View file

@ -12,7 +12,7 @@ public class Configs {
public static final ServerConfig SERVER_CONFIG = new ServerConfig();
public static final PathConfig GENERATOR_CONFIG = new PathConfig(BCLib.MOD_ID, "generator");
public static final PathConfig GENERATOR_CONFIG = new PathConfig(BCLib.MOD_ID, "generator", false);
public static final PathConfig MAIN_CONFIG = new PathConfig(BCLib.MOD_ID, "main", true, true);
public static final String MAIN_PATCH_CATEGORY = "patches";
@ -23,5 +23,6 @@ public class Configs {
public static void save() {
MAIN_CONFIG.saveChanges();
RECIPE_CONFIG.saveChanges();
GENERATOR_CONFIG.saveChanges();
}
}

View file

@ -12,10 +12,11 @@ public class ServerConfig extends NamedPathConfig {
public static final DependendConfigToken<Boolean> OFFER_FILES = DependendConfigToken.Boolean(true, "offerFiles", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_MODS = DependendConfigToken.Boolean(true, "offerMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_ALL_MODS = DependendConfigToken.Boolean(false, "offerAllMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(OFFER_MODS));
public static final DependendConfigToken<Boolean> SEND_ALL_MOD_INFO = DependendConfigToken.Boolean(true, "sendAllModInfo", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final DependendConfigToken<Boolean> SEND_ALL_MOD_INFO = DependendConfigToken.Boolean(false, "sendAllModInfo", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final ConfigToken<List<String>> ADDITIONAL_MODS = ConfigToken.StringArray(new ArrayList<>(0), "additionalMods", AutoSync.SYNC_CATEGORY);
public static final ConfigToken<List<String>> EXCLUDED_MODS = ConfigToken.StringArray(new ArrayList<>(0), "excludeMods", AutoSync.SYNC_CATEGORY);
public ServerConfig() {
@ -45,8 +46,5 @@ public class ServerConfig extends NamedPathConfig {
public boolean isOfferingInfosForMods() {
return get(SEND_ALL_MOD_INFO) /*&& isAllowingAutoSync()*/;
}
public String[] additionalModsForSync() {
return new String[0];
}
}

View file

@ -3,7 +3,7 @@ package ru.bclib.gui.gridlayout;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import ru.bclib.util.TriConsumer;
import ru.bclib.interfaces.TriConsumer;
import java.util.List;
import java.util.function.Function;

View file

@ -5,8 +5,8 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.components.AbstractWidget;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import ru.bclib.interfaces.TriConsumer;
import ru.bclib.util.Pair;
import ru.bclib.util.TriConsumer;
import java.util.LinkedList;
import java.util.List;
@ -88,6 +88,7 @@ public class GridLayout extends GridColumn {
public static final int COLOR_RED = 0xFFDB1F48;
public static final int COLOR_CYAN = 0xFF01949A;
public static final int COLOR_GREEN = 0xFF00FF00;
public static final int COLOR_DARK_GREEN = 0xFF007F00;
public static final int COLOR_YELLOW = 0xFFFAD02C;
public static final int COLOR_BLUE = 0xFF0000FF;
public static final int COLOR_GRAY = 0xFF7F7F7F;

View file

@ -9,8 +9,6 @@ import com.mojang.blaze3d.vertex.VertexFormat;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.AbstractSelectionList;
import net.minecraft.client.gui.components.OptionsList;
import net.minecraft.client.gui.components.Widget;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;

View file

@ -1,6 +1,5 @@
package ru.bclib.gui.screens;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screens.Screen;

View file

@ -0,0 +1,60 @@
package ru.bclib.gui.screens;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import ru.bclib.gui.gridlayout.GridColumn;
import ru.bclib.gui.gridlayout.GridLayout;
import ru.bclib.gui.gridlayout.GridRow;
@Environment(EnvType.CLIENT)
public class LevelFixErrorScreen extends BCLibScreen {
private final String[] errors;
final Listener onContinue;
public LevelFixErrorScreen(Screen parent, String[] errors, Listener onContinue) {
super(parent, new TranslatableComponent("title.bclib.datafixer.error"), 10, true);
this.errors = errors;
this.onContinue = onContinue;
}
@Override
protected void initLayout() {
grid.addSpacerRow();
grid.addRow().addMessage(new TranslatableComponent("message.bclib.datafixer.error"), font, GridLayout.Alignment.CENTER);
grid.addSpacerRow(8);
GridRow row = grid.addRow();
row.addSpacer(10);
GridColumn col = row.addColumn(300, GridLayout.GridValueType.CONSTANT);
for (String error : errors){
TextComponent dash = new TextComponent("-");
row = col.addRow();
row.addString(dash, this);
row.addSpacer(4);
row.addString(new TextComponent(error), this);
}
grid.addSpacerRow(8);
row = grid.addRow();
row.addFiller();
row.addButton(new TranslatableComponent("title.bclib.datafixer.error.continue"), 0.5f, 20, font, (n)-> {
onClose();
onContinue.doContinue(true);
});
row.addSpacer();
row.addButton(CommonComponents.GUI_CANCEL, 20, font, (n)-> {
this.minecraft.setScreen(null);
});
row.addFiller();
}
@Environment(EnvType.CLIENT)
public interface Listener {
void doContinue(boolean markFixed);
}
}

View file

@ -73,13 +73,14 @@ public class ModListScreen extends BCLibScreen {
public static void addModDesc(GridColumn grid, java.util.List<ModUtil.ModInfo> mods, HelloClient.IServerModMap serverInfo, GridScreen parent) {
final int STATE_OK = 0;
final int STATE_MISSING = 1;
final int STATE_SERVER_MISSING = 2;
final int STATE_VERSION = 3;
final int STATE_SERVER_MISSING_CLIENT_MOD = 4;
final int STATE_VERSION_NOT_OFFERED = 5;
final int STATE_MISSING_NOT_OFFERED = 6;
final int STATE_OK = 6;
final int STATE_SERVER_MISSING_CLIENT_MOD = 5;
final int STATE_MISSING_NOT_OFFERED = 4;
final int STATE_VERSION_NOT_OFFERED = 3;
final int STATE_VERSION = 2;
final int STATE_SERVER_MISSING = 1;
final int STATE_MISSING = 0;
List<Triple<String, Integer, String>> items = new LinkedList<>();
if (serverInfo!=null) {
@ -141,7 +142,7 @@ public class ModListScreen extends BCLibScreen {
});
items.stream()
.sorted(Comparator.comparing(a -> a.first.toLowerCase(Locale.ROOT)))
.sorted(Comparator.comparing(a -> a.second + a.first.toLowerCase(Locale.ROOT)))
.forEach(t -> {
final String name = t.first;
final int state = t.second;
@ -160,12 +161,14 @@ public class ModListScreen extends BCLibScreen {
color = GridLayout.COLOR_YELLOW;
}
} else if (state==STATE_SERVER_MISSING || state == STATE_SERVER_MISSING_CLIENT_MOD) {
typeText = "[NOT ON SERVER]";
if (state == STATE_SERVER_MISSING_CLIENT_MOD) {
color = GridLayout.COLOR_YELLOW;
color = GridLayout.COLOR_CYAN;
typeText = "[OK]";
} else {
typeText = "[NOT ON SERVER]";
}
} else {
color = GridLayout.COLOR_CYAN;
color = GridLayout.COLOR_DARK_GREEN;
typeText = "[OK]";
}
TextComponent dash = new TextComponent("-");
@ -208,7 +211,6 @@ public class ModListScreen extends BCLibScreen {
row.addFiller();
row.addButton(buttonTitle, 20, font, (n)-> {
onClose();
System.out.println("Closing");
});
row.addFiller();
}

View file

@ -7,7 +7,6 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridRow;
import ru.bclib.gui.gridlayout.GridScreen;
@Environment(EnvType.CLIENT)
public class WarnBCLibVersionMismatch extends BCLibScreen {

View file

@ -0,0 +1,36 @@
package ru.bclib.interfaces;
import ru.bclib.recipes.AnvilRecipe;
import java.util.List;
public interface AnvilScreenHandlerExtended {
void be_updateCurrentRecipe(AnvilRecipe recipe);
AnvilRecipe be_getCurrentRecipe();
List<AnvilRecipe> be_getRecipes();
default void be_nextRecipe() {
List<AnvilRecipe> recipes = be_getRecipes();
if (recipes.size() < 2) return;
AnvilRecipe current = be_getCurrentRecipe();
int i = recipes.indexOf(current) + 1;
if (i >= recipes.size()) {
i = 0;
}
be_updateCurrentRecipe(recipes.get(i));
}
default void be_previousRecipe() {
List<AnvilRecipe> recipes = be_getRecipes();
if (recipes.size() < 2) return;
AnvilRecipe current = be_getCurrentRecipe();
int i = recipes.indexOf(current) - 1;
if (i <= 0) {
i = recipes.size() - 1;
}
be_updateCurrentRecipe(recipes.get(i));
}
}

View file

@ -0,0 +1,8 @@
package ru.bclib.interfaces;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.biome.Biome;
public interface BiomeSetter {
public void bclib_setBiome(Biome biome, BlockPos pos);
}

View file

@ -1,4 +1,4 @@
package ru.bclib.util;
package ru.bclib.interfaces;
@FunctionalInterface
public interface TriConsumer<A, B, C> {

View file

@ -0,0 +1,4 @@
package ru.bclib.interfaces;
public interface UnknownReceipBookCategory {
}

View file

@ -0,0 +1,21 @@
package ru.bclib.items.tool;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.ShearsItem;
import ru.bclib.api.TagAPI;
public class BaseShearsItem extends ShearsItem {
public BaseShearsItem(Properties properties) {
super(properties);
}
public static boolean isShear(ItemStack itemStack, Item item){
if (item == Items.SHEARS){
return itemStack.is(item) | itemStack.is(TagAPI.ITEM_COMMON_SHEARS) || itemStack.is(TagAPI.ITEM_SHEARS);
} else {
return itemStack.is(item);
}
}
}

View file

@ -0,0 +1,95 @@
package ru.bclib.mixin.client;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.inventory.AnvilScreen;
import net.minecraft.client.gui.screens.inventory.ItemCombinerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.AnvilMenu;
import net.minecraft.world.item.ItemStack;
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 ru.bclib.interfaces.AnvilScreenHandlerExtended;
import java.util.List;
@Mixin(AnvilScreen.class)
public class AnvilScreenMixin extends ItemCombinerScreen<AnvilMenu> {
@Shadow
private EditBox name;
private final List<AbstractWidget> be_buttons = Lists.newArrayList();
public AnvilScreenMixin(AnvilMenu handler, Inventory playerInventory, Component title, ResourceLocation texture) {
super(handler, playerInventory, title, texture);
}
@Inject(method = "subInit", at = @At("TAIL"))
protected void be_subInit(CallbackInfo info) {
int x = (width - imageWidth) / 2;
int y = (height - imageHeight) / 2;
be_buttons.clear();
be_buttons.add(new Button(x + 8, y + 45, 15, 20, new TextComponent("<"), b -> be_previousRecipe()));
be_buttons.add(new Button(x + 154, y + 45, 15, 20, new TextComponent(">"), b -> be_nextRecipe()));
}
@Inject(method = "renderFg", at = @At("TAIL"))
protected void be_renderForeground(PoseStack matrices, int mouseX, int mouseY, float delta, CallbackInfo info) {
be_buttons.forEach(button -> {
button.render(matrices, mouseX, mouseY, delta);
});
}
@Inject(method = "slotChanged", at = @At("HEAD"), cancellable = true)
public void be_onSlotUpdate(AbstractContainerMenu handler, int slotId, ItemStack stack, CallbackInfo info) {
AnvilScreenHandlerExtended anvilHandler = (AnvilScreenHandlerExtended) handler;
if (anvilHandler.be_getCurrentRecipe() != null) {
if (anvilHandler.be_getRecipes().size() > 1) {
be_buttons.forEach(button -> button.visible = true);
}
else {
be_buttons.forEach(button -> button.visible = false);
}
name.setValue("");
info.cancel();
}
else {
be_buttons.forEach(button -> button.visible = false);
}
}
private void be_nextRecipe() {
((AnvilScreenHandlerExtended) menu).be_nextRecipe();
}
private void be_previousRecipe() {
((AnvilScreenHandlerExtended) menu).be_previousRecipe();
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (minecraft != null) {
for (AbstractWidget elem : be_buttons) {
if (elem.visible && elem.mouseClicked(mouseX, mouseY, button)) {
if (minecraft.gameMode != null) {
int i = be_buttons.indexOf(elem);
minecraft.gameMode.handleInventoryButtonClick(menu.containerId, i);
return true;
}
}
}
}
return super.mouseClicked(mouseX, mouseY, button);
}
}

View file

@ -23,6 +23,7 @@ public class BackgroundRendererMixin {
private static final MutableBlockPos BCLIB_LAST_POS = new MutableBlockPos(0, -100, 0);
private static final MutableBlockPos BCLIB_MUT_POS = new MutableBlockPos();
private static final float[] BCLIB_FOG_DENSITY = new float[8];
//private static boolean isEnd;
@Shadow
private static float fogRed;

View file

@ -1,2 +0,0 @@
package ru.bclib.mixin.client;public class ClientLevelMixin {
}

View file

@ -0,0 +1,20 @@
package ru.bclib.mixin.client;
import net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.world.item.crafting.Recipe;
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 ru.bclib.interfaces.UnknownReceipBookCategory;
@Mixin(ClientRecipeBook.class)
public abstract class ClientRecipeBookMixin {
@Inject(method = "getCategory", at = @At("HEAD"), cancellable = true)
private static void be_getGroupForRecipe(Recipe<?> recipe, CallbackInfoReturnable<RecipeBookCategories> info) {
if (recipe instanceof UnknownReceipBookCategory) {
info.setReturnValue(RecipeBookCategories.UNKNOWN);
}
}
}

View file

@ -1,18 +0,0 @@
package ru.bclib.mixin.client;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundLoginPacket;
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.CallbackInfo;
@Mixin(ClientboundLoginPacket.class)
public abstract class ClientboundLoginPacketMixin {
@Inject(method = "handle", cancellable = true, at=@At("HEAD"))
public void bclib_handle(ClientGamePacketListener clientGamePacketListener, CallbackInfo ci){
//cLevel.setBCLibDidSendHello();
// DataExchangeAPI.sendOnEnter();
// ci.cancel();
}
}

View file

@ -100,8 +100,7 @@ public abstract class MinecraftMixin {
DataExchangeAPI.prepareServerside();
if (DataFixerAPI.fixData(this.levelSource, levelID, true, (appliedFixes) -> {
bclib_doLoadLevel_BACKUP(levelID, RegistryAccess.builtin(), Minecraft::loadDataPacks, Minecraft::loadWorldData, false);
//this.doLoadLevel(levelID, RegistryAccess.builtin(), Minecraft::loadDataPacks, Minecraft::loadWorldData, false, Minecraft.ExperimentalDialogType.BACKUP);
this.doLoadLevel(levelID, RegistryAccess.builtin(), Minecraft::loadDataPacks, Minecraft::loadWorldData, false, appliedFixes?ExperimentalDialogType.NONE:ExperimentalDialogType.BACKUP);
})) {
ci.cancel();
}

View file

@ -9,6 +9,8 @@ import net.minecraft.world.inventory.DataSlot;
import net.minecraft.world.inventory.ItemCombinerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
@ -17,10 +19,22 @@ 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import ru.bclib.blocks.BaseAnvilBlock;
import ru.bclib.blocks.LeveledAnvilBlock;
import ru.bclib.interfaces.AnvilScreenHandlerExtended;
import ru.bclib.recipes.AnvilRecipe;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Mixin(AnvilMenu.class)
public abstract class AnvilMenuMixin extends ItemCombinerMenu {
public abstract class AnvilMenuMixin extends ItemCombinerMenu implements AnvilScreenHandlerExtended {
private List<AnvilRecipe> be_recipes = Collections.emptyList();
private AnvilRecipe be_currentRecipe;
private DataSlot anvilLevel;
@Shadow
private int repairItemCountCost;
@ -31,9 +45,64 @@ public abstract class AnvilMenuMixin extends ItemCombinerMenu {
public AnvilMenuMixin(@Nullable MenuType<?> menuType, int i, Inventory inventory, ContainerLevelAccess containerLevelAccess) {
super(menuType, i, inventory, containerLevelAccess);
}
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/inventory/ContainerLevelAccess;)V", at = @At("TAIL"))
public void be_initAnvilLevel(int syncId, Inventory inventory, ContainerLevelAccess context, CallbackInfo info) {
this.anvilLevel = addDataSlot(DataSlot.standalone());
if (context != ContainerLevelAccess.NULL) {
int level = context.evaluate((world, blockPos) -> {
Block anvilBlock = world.getBlockState(blockPos).getBlock();
if (anvilBlock instanceof LeveledAnvilBlock) {
return ((LeveledAnvilBlock) anvilBlock).getCraftingLevel();
}
return 1;
}, 1);
anvilLevel.set(level);
}
else {
anvilLevel.set(1);
}
}
@Shadow
public abstract void createResult();
@Inject(method = "mayPickup", at = @At("HEAD"), cancellable = true)
protected void be_canTakeOutput(Player player, boolean present, CallbackInfoReturnable<Boolean> info) {
if (be_currentRecipe != null) {
info.setReturnValue(be_currentRecipe.checkHammerDurability(inputSlots, player));
}
}
@Inject(method = "onTake", at = @At("HEAD"), cancellable = true)
protected void bclib_onTakeAnvilOutput(Player player, ItemStack stack, CallbackInfo info) {
if (be_currentRecipe != null) {
inputSlots.getItem(0).shrink(be_currentRecipe.getInputCount());
stack = be_currentRecipe.craft(inputSlots, player);
slotsChanged(inputSlots);
access.execute((world, blockPos) -> {
final BlockState anvilState = world.getBlockState(blockPos);
final Block anvilBlock = anvilState.getBlock();
if (anvilBlock instanceof BaseAnvilBlock) {
final BaseAnvilBlock anvil = (BaseAnvilBlock) anvilBlock;
if (!player.getAbilities().instabuild && anvilState.is(BlockTags.ANVIL) && player.getRandom().nextDouble() < 0.1) {
BlockState damagedState = anvil.damageAnvilUse(anvilState, player.getRandom());
if (damagedState == null) {
world.removeBlock(blockPos, false);
world.levelEvent(1029, blockPos, 0);
} else {
world.setBlock(blockPos, damagedState, 2);
world.levelEvent(1030, blockPos, 0);
}
} else {
world.levelEvent(1030, blockPos, 0);
}
}
});
info.cancel();
return;
}
this.access.execute((level, blockPos) -> {
BlockState blockState = level.getBlockState(blockPos);
if (blockState.getBlock() instanceof BaseAnvilBlock) {
@ -77,4 +146,68 @@ public abstract class AnvilMenuMixin extends ItemCombinerMenu {
}
});
}
@Inject(method = "createResult", at = @At("HEAD"), cancellable = true)
public void be_updateOutput(CallbackInfo info) {
RecipeManager recipeManager = this.player.level.getRecipeManager();
be_recipes = recipeManager.getRecipesFor(AnvilRecipe.TYPE, inputSlots, player.level);
if (be_recipes.size() > 0) {
int anvilLevel = this.anvilLevel.get();
be_recipes = be_recipes.stream()
.filter(recipe -> anvilLevel >= recipe.getAnvilLevel())
.collect(Collectors.toList());
if (be_recipes.size() > 0) {
if (be_currentRecipe == null || !be_recipes.contains(be_currentRecipe)) {
be_currentRecipe = be_recipes.get(0);
}
be_updateResult();
info.cancel();
}
else {
be_currentRecipe = null;
}
}
}
@Inject(method = "setItemName", at = @At("HEAD"), cancellable = true)
public void be_setNewItemName(String string, CallbackInfo info) {
if (be_currentRecipe != null) {
info.cancel();
}
}
@Override
public boolean clickMenuButton(Player player, int id) {
if (id == 0) {
be_previousRecipe();
return true;
}
else if (id == 1) {
be_nextRecipe();
return true;
}
return super.clickMenuButton(player, id);
}
private void be_updateResult() {
if (be_currentRecipe == null) return;
resultSlots.setItem(0, be_currentRecipe.assemble(inputSlots));
broadcastChanges();
}
@Override
public void be_updateCurrentRecipe(AnvilRecipe recipe) {
this.be_currentRecipe = recipe;
be_updateResult();
}
@Override
public AnvilRecipe be_getCurrentRecipe() {
return be_currentRecipe;
}
@Override
public List<AnvilRecipe> be_getRecipes() {
return be_recipes;
}
}

View file

@ -0,0 +1,134 @@
package ru.bclib.mixin.common;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.BlockPos;
import net.minecraft.util.BitStorage;
import net.minecraft.util.Mth;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import ru.bclib.BCLib;
import ru.bclib.interfaces.BiomeSetter;
import java.lang.reflect.Field;
@Mixin(ChunkBiomeContainer.class)
public class ChunkBiomeContainerMixin implements BiomeSetter {
private static boolean bclib_hasHydrogen = FabricLoader.getInstance().isModLoaded("hydrogen");
@Final
@Shadow
private Biome[] biomes;
@Final
@Shadow
private static int WIDTH_BITS;
@Final
@Shadow
private static int HORIZONTAL_MASK;
@Override
public void bclib_setBiome(Biome biome, BlockPos pos) {
int biomeX = pos.getX() >> 2;
int biomeY = pos.getY() >> 2;
int biomeZ = pos.getZ() >> 2;
int index = be_getArrayIndex(biomeX, biomeY, biomeZ);
if (bclib_hasHydrogen && be_shouldWriteToHydrogen()) {
try {
ChunkBiomeContainer self = (ChunkBiomeContainer) (Object) this;
BitStorage storage = be_getHydrogenStorage(self);
Biome[] palette = be_getHydrogenPalette(self);
int paletteIndex = be_getHydrogenPaletteIndex(biome, palette);
if (paletteIndex == -1) {
Biome[] newPalette = new Biome[palette.length + 1];
System.arraycopy(palette, 0, newPalette, 0, palette.length);
paletteIndex = palette.length;
palette = newPalette;
palette[paletteIndex] = biome;
be_setHydrogenPalette(self, palette);
}
try {
storage.set(index, paletteIndex);
}
catch (Exception e) {
int size = storage.getSize();
int bits = Mth.ceillog2(palette.length);
BitStorage newStorage = new BitStorage(bits, size);
for (int i = 0; i < size; i++) {
newStorage.set(i, storage.get(i));
}
storage = newStorage;
storage.set(index, paletteIndex);
be_setHydrogenStorage(self, storage);
}
}
catch (Exception e) {
BCLib.LOGGER.warning(e.getLocalizedMessage());
}
return;
}
biomes[index] = biome;
}
@Shadow
@Final
private int quartMinY;
@Shadow
@Final
private int quartHeight;
private boolean be_shouldWriteToHydrogen() {
try {
Field field = ChunkBiomeContainer.class.getDeclaredField("intArray");
return field != null;
}
catch (NoSuchFieldException e) {
return false;
}
}
private int be_getArrayIndex(int biomeX, int biomeY, int biomeZ) {
int i = biomeX & HORIZONTAL_MASK;
int j = Mth.clamp(biomeY - this.quartMinY, 0, this.quartHeight);
int k = biomeZ & HORIZONTAL_MASK;
return j << WIDTH_BITS + WIDTH_BITS | k << WIDTH_BITS | i;
}
private Field be_getField(String name) throws Exception {
Field field = ChunkBiomeContainer.class.getDeclaredField(name);
field.setAccessible(true);
return field;
}
private BitStorage be_getHydrogenStorage(ChunkBiomeContainer container) throws Exception {
return (BitStorage) be_getField("intArray").get(container);
}
private Biome[] be_getHydrogenPalette(ChunkBiomeContainer container) throws Exception {
return (Biome[]) be_getField("palette").get(container);
}
private int be_getHydrogenPaletteIndex(Biome biome, Biome[] palette) {
int index = -1;
for (int i = 0; i < palette.length; i++) {
if (palette[i] == biome) {
index = i;
break;
}
}
return index;
}
private void be_setHydrogenPalette(ChunkBiomeContainer container, Biome[] palette) throws Exception {
be_getField("palette").set(container, palette);
}
private void be_setHydrogenStorage(ChunkBiomeContainer container, BitStorage storage) throws Exception {
be_getField("intArray").set(container, storage);
}
}

View file

@ -0,0 +1,31 @@
package ru.bclib.mixin.common;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.CraftingMenu;
import net.minecraft.world.level.block.CraftingTableBlock;
import net.minecraft.world.level.block.state.BlockState;
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 ru.bclib.api.TagAPI;
@Mixin(CraftingMenu.class)
public abstract class CraftingMenuMixin {
@Final
@Shadow
private ContainerLevelAccess access;
@Inject(method = "stillValid", at = @At("HEAD"), cancellable = true)
private void bclib_stillValid(Player player, CallbackInfoReturnable<Boolean> info) {
if (access.evaluate((world, pos) -> {
BlockState state = world.getBlockState(pos);
return state.getBlock() instanceof CraftingTableBlock || state.is(TagAPI.BLOCK_WORKBENCHES);
}, true)) {
info.setReturnValue(true);
}
}
}

View file

@ -12,24 +12,29 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import ru.bclib.world.generator.BCLibEndBiomeSource;
import ru.bclib.world.generator.BCLibNetherBiomeSource;
import ru.bclib.world.generator.GeneratorOptions;
@Mixin(value = DimensionType.class, priority = 100)
public class DimensionTypeMixin {
@Inject(method = "defaultNetherGenerator", at = @At("HEAD"), cancellable = true)
private static void be_replaceNetherBiomeSource(Registry<Biome> biomeRegistry, Registry<NoiseGeneratorSettings> chunkGeneratorSettingsRegistry, long seed, CallbackInfoReturnable<ChunkGenerator> info) {
info.setReturnValue(new NoiseBasedChunkGenerator(
new BCLibNetherBiomeSource(biomeRegistry, seed),
seed,
() -> chunkGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.NETHER)
));
if (GeneratorOptions.customNetherBiomeSource()) {
info.setReturnValue(new NoiseBasedChunkGenerator(
new BCLibNetherBiomeSource(biomeRegistry, seed),
seed,
() -> chunkGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.NETHER)
));
}
}
@Inject(method = "defaultEndGenerator", at = @At("HEAD"), cancellable = true)
private static void be_replaceEndBiomeSource(Registry<Biome> biomeRegistry, Registry<NoiseGeneratorSettings> chunkGeneratorSettingsRegistry, long seed, CallbackInfoReturnable<ChunkGenerator> info) {
info.setReturnValue(new NoiseBasedChunkGenerator(
new BCLibEndBiomeSource(biomeRegistry, seed),
seed,
() -> chunkGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.END)
));
if (GeneratorOptions.customEndBiomeSource()) {
info.setReturnValue(new NoiseBasedChunkGenerator(
new BCLibEndBiomeSource(biomeRegistry, seed),
seed,
() -> chunkGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.END)
));
}
}
}

View file

@ -0,0 +1,23 @@
package ru.bclib.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.piston.PistonBaseBlock;
import net.minecraft.world.level.block.state.BlockState;
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 ru.bclib.api.TagAPI;
@Mixin(PistonBaseBlock.class)
public class PistonBaseBlockMixin {
@Inject(method="isPushable", at=@At("HEAD"), cancellable = true)
private static void bclib_isPushable(BlockState blockState, Level level, BlockPos blockPos, Direction direction, boolean bl, Direction direction2, CallbackInfoReturnable<Boolean> cir){
if (blockState.is(TagAPI.BLOCK_IMMOBILE)){
cir.setReturnValue(false);
cir.cancel();
}
}
}

View file

@ -0,0 +1,33 @@
package ru.bclib.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockBehaviour.StatePredicate;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.portal.PortalShape;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.api.TagAPI;
@Mixin(PortalShape.class)
public class PortalShapeMixin {
@Redirect(method="getDistanceUntilEdgeAboveFrame", at=@At(value="INVOKE", target="Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;test(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z"))
private boolean be_getDistanceUntilEdgeAboveFrame(StatePredicate statePredicate, BlockState blockState, BlockGetter blockGetter, BlockPos blockPos){
return be_FRAME(statePredicate, blockState, blockGetter, blockPos);
}
@Redirect(method="hasTopFrame", at=@At(value="INVOKE", target="Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;test(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z"))
private boolean be_hasTopFrame(StatePredicate statePredicate, BlockState blockState, BlockGetter blockGetter, BlockPos blockPos){
return be_FRAME(statePredicate, blockState, blockGetter, blockPos);
}
@Redirect(method="getDistanceUntilTop", at=@At(value="INVOKE", target="Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;test(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z"))
private boolean be_getDistanceUntilTop(StatePredicate statePredicate, BlockState blockState, BlockGetter blockGetter, BlockPos blockPos){
return be_FRAME(statePredicate, blockState, blockGetter, blockPos);
}
private static boolean be_FRAME(StatePredicate FRAME, BlockState state, BlockGetter getter, BlockPos pos){
return state.is(TagAPI.BLOCK_NETHER_PORTAL_FRAME) || FRAME.test(state, getter, pos);
}
}

View file

@ -33,6 +33,7 @@ public abstract class ServerLevelMixin extends Level {
@Inject(method = "<init>*", at = @At("TAIL"))
private void bclib_onServerWorldInit(MinecraftServer server, Executor workerExecutor, LevelStorageSource.LevelStorageAccess session, ServerLevelData properties, ResourceKey<Level> registryKey, DimensionType dimensionType, ChunkProgressListener worldGenerationProgressListener, ChunkGenerator chunkGenerator, boolean debugWorld, long l, List<CustomSpawner> list, boolean bl, CallbackInfo info) {
BiomeAPI.initRegistry(server);
BiomeAPI.applyModifications(ServerLevel.class.cast(this));
if (bclib_lastWorld != null && bclib_lastWorld.equals(session.getLevelId())) {
return;

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.BeehiveBlock;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(BeehiveBlock.class)
public class BeehiveBlockMixin {
@Redirect(method="use", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_useProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.DiggingEnchantment;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(DiggingEnchantment.class)
public class DiggingEnchantmentMixin {
@Redirect(method="canEnchant", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_mobInteractProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.entity.animal.MushroomCow;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(MushroomCow.class)
public class MushroomCowMixin {
@Redirect(method="mobInteract", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_mobInteractProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.PumpkinBlock;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(PumpkinBlock.class)
public abstract class PumpkinBlockMixin {
@Redirect(method="use", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_useProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.entity.animal.Sheep;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(Sheep.class)
public class SheepMixin {
@Redirect(method="mobInteract", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_mobInteractProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.entity.animal.SnowGolem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(SnowGolem.class)
public class SnowGolemMixin {
@Redirect(method="mobInteract", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_mobInteractProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,17 @@
package ru.bclib.mixin.common.shears;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.TripWireBlock;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import ru.bclib.items.tool.BaseShearsItem;
@Mixin(TripWireBlock.class)
public class TripWireBlockMixin {
@Redirect(method="playerWillDestroy", at=@At(value="INVOKE", target="Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
public boolean bn_useProxy(ItemStack itemStack, Item item){
return BaseShearsItem.isShear(itemStack, item);
}
}

View file

@ -0,0 +1,334 @@
package ru.bclib.recipes;
import com.google.gson.JsonObject;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.TagParser;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.Tag;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TieredItem;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import ru.bclib.BCLib;
import ru.bclib.api.TagAPI;
import ru.bclib.config.PathConfig;
import ru.bclib.interfaces.UnknownReceipBookCategory;
import ru.bclib.util.ItemUtil;
import ru.bclib.util.RecipeHelper;
import java.util.Objects;
public class AnvilRecipe implements Recipe<Container>, UnknownReceipBookCategory {
public final static String GROUP = "smithing";
public final static RecipeType<AnvilRecipe> TYPE = BCLRecipeManager.registerType(BCLib.MOD_ID, GROUP);
public final static Serializer SERIALIZER = BCLRecipeManager.registerSerializer(
BCLib.MOD_ID,
GROUP,
new Serializer()
);
public final static ResourceLocation ID = BCLib.makeID(GROUP);
private final ResourceLocation id;
private final Ingredient input;
private final ItemStack output;
private final int damage;
private final int toolLevel;
private final int anvilLevel;
private final int inputCount;
public AnvilRecipe(ResourceLocation identifier, Ingredient input, ItemStack output, int inputCount, int toolLevel, int anvilLevel, int damage) {
this.id = identifier;
this.input = input;
this.output = output;
this.toolLevel = toolLevel;
this.anvilLevel = anvilLevel;
this.inputCount = inputCount;
this.damage = damage;
}
public static Builder create(String id) {
return create(BCLib.makeID(id));
}
public static Builder create(ResourceLocation id) {
Builder.INSTANCE.id = id;
Builder.INSTANCE.input = null;
Builder.INSTANCE.output = null;
Builder.INSTANCE.inputCount = 1;
Builder.INSTANCE.toolLevel = 1;
Builder.INSTANCE.anvilLevel = 1;
Builder.INSTANCE.damage = 1;
Builder.INSTANCE.alright = true;
Builder.INSTANCE.exist = true;
return Builder.INSTANCE;
}
@Override
public RecipeSerializer<?> getSerializer() {
return SERIALIZER;
}
@Override
public ItemStack getResultItem() {
return this.output;
}
@Override
public boolean matches(Container craftingInventory, Level world) {
return this.matches(craftingInventory);
}
@Override
public ItemStack assemble(Container craftingInventory) {
return this.output.copy();
}
public ItemStack craft(Container craftingInventory, Player player) {
if (!player.isCreative()) {
if (!checkHammerDurability(craftingInventory, player)) return ItemStack.EMPTY;
ItemStack hammer = craftingInventory.getItem(1);
hammer.hurtAndBreak(this.damage, player, entity -> entity.broadcastBreakEvent((InteractionHand) null));
}
return this.assemble(craftingInventory);
}
public boolean checkHammerDurability(Container craftingInventory, Player player) {
if (player.isCreative()) return true;
ItemStack hammer = craftingInventory.getItem(1);
int damage = hammer.getDamageValue() + this.damage;
return damage < hammer.getMaxDamage();
}
public boolean matches(Container craftingInventory) {
ItemStack hammer = craftingInventory.getItem(1);
if (hammer.isEmpty() || !TagAPI.ITEM_HAMMERS.contains(hammer.getItem())) {
return false;
}
ItemStack material = craftingInventory.getItem(0);
int materialCount = material.getCount();
int level = ((TieredItem) hammer.getItem()).getTier().getLevel();
return this.input.test(craftingInventory.getItem(0)) && materialCount >= this.inputCount && level >= this.toolLevel;
}
public int getDamage() {
return this.damage;
}
public int getInputCount() {
return this.inputCount;
}
public int getAnvilLevel() {
return this.anvilLevel;
}
@Override
public NonNullList<Ingredient> getIngredients() {
NonNullList<Ingredient> defaultedList = NonNullList.create();
defaultedList.add(Ingredient.of(TagAPI.ITEM_HAMMERS.getValues()
.stream()
.filter(hammer -> ((TieredItem) hammer).getTier()
.getLevel() >= toolLevel)
.map(ItemStack::new)));
defaultedList.add(input);
return defaultedList;
}
@Override
@Environment(EnvType.CLIENT)
public boolean canCraftInDimensions(int width, int height) {
return true;
}
@Override
public ResourceLocation getId() {
return this.id;
}
@Override
public RecipeType<?> getType() {
return TYPE;
}
@Override
public boolean isSpecial() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AnvilRecipe that = (AnvilRecipe) o;
return damage == that.damage && toolLevel == that.toolLevel && id.equals(that.id) && input.equals(that.input) && output
.equals(that.output);
}
@Override
public int hashCode() {
return Objects.hash(id, input, output, damage, toolLevel);
}
@Override
public String toString() {
return "AnvilRecipe [" + id + "]";
}
public static class Builder {
private final static Builder INSTANCE = new Builder();
private ResourceLocation id;
private Ingredient input;
private ItemStack output;
private int inputCount = 1;
private int toolLevel = 1;
private int anvilLevel = 1;
private int damage = 1;
private boolean alright;
private boolean exist;
private Builder() { }
public Builder setInput(ItemLike... inputItems) {
this.alright &= RecipeHelper.exists(inputItems);
this.setInput(Ingredient.of(inputItems));
return this;
}
public Builder setInput(Tag<Item> inputTag) {
this.setInput(Ingredient.of(inputTag));
return this;
}
public Builder setInput(Ingredient ingredient) {
this.input = ingredient;
return this;
}
public Builder setInputCount(int count) {
this.inputCount = count;
return this;
}
public Builder setOutput(ItemLike output) {
return this.setOutput(output, 1);
}
public Builder setOutput(ItemLike output, int amount) {
this.alright &= RecipeHelper.exists(output);
this.output = new ItemStack(output, amount);
return this;
}
public Builder setToolLevel(int level) {
this.toolLevel = level;
return this;
}
public Builder setAnvilLevel(int level) {
this.anvilLevel = level;
return this;
}
public Builder setDamage(int damage) {
this.damage = damage;
return this;
}
public Builder checkConfig(PathConfig config) {
exist |= config.getBoolean("anvil", id.getPath(), true);
return this;
}
public void build() {
if (exist) {
if (input == null) {
BCLib.LOGGER.warning("Input for Anvil recipe can't be 'null', recipe {} will be ignored!", id);
return;
}
if (output == null) {
BCLib.LOGGER.warning("Output for Anvil recipe can't be 'null', recipe {} will be ignored!", id);
return;
}
if (BCLRecipeManager.getRecipe(TYPE, id) != null) {
BCLib.LOGGER.warning("Can't add Anvil recipe! Id {} already exists!", id);
return;
}
if (!alright) {
BCLib.LOGGER.debug("Can't add Anvil recipe {}! Ingeredient or output not exists.", id);
return;
}
BCLRecipeManager.addRecipe(
TYPE,
new AnvilRecipe(id, input, output, inputCount, toolLevel, anvilLevel, damage)
);
}
}
}
public static class Serializer implements RecipeSerializer<AnvilRecipe> {
@Override
public AnvilRecipe fromJson(ResourceLocation id, JsonObject json) {
Ingredient input = Ingredient.fromJson(json.get("input"));
JsonObject result = GsonHelper.getAsJsonObject(json, "result");
ItemStack output = ItemUtil.fromJsonRecipe(result);
if (output == null) {
throw new IllegalStateException("Output item does not exists!");
}
if (result.has("nbt")) {
try {
String nbtData = GsonHelper.getAsString(result, "nbt");
CompoundTag nbt = TagParser.parseTag(nbtData);
output.setTag(nbt);
}
catch (CommandSyntaxException ex) {
BCLib.LOGGER.warning("Error parse nbt data for output.", ex);
}
}
int inputCount = GsonHelper.getAsInt(json, "inputCount", 1);
int toolLevel = GsonHelper.getAsInt(json, "toolLevel", 1);
int anvilLevel = GsonHelper.getAsInt(json, "anvilLevel", 1);
int damage = GsonHelper.getAsInt(json, "damage", 1);
return new AnvilRecipe(id, input, output, inputCount, toolLevel, anvilLevel, damage);
}
@Override
public AnvilRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf packetBuffer) {
Ingredient input = Ingredient.fromNetwork(packetBuffer);
ItemStack output = packetBuffer.readItem();
int inputCount = packetBuffer.readVarInt();
int toolLevel = packetBuffer.readVarInt();
int anvilLevel = packetBuffer.readVarInt();
int damage = packetBuffer.readVarInt();
return new AnvilRecipe(id, input, output, inputCount, toolLevel, anvilLevel, damage);
}
@Override
public void toNetwork(FriendlyByteBuf packetBuffer, AnvilRecipe recipe) {
recipe.input.toNetwork(packetBuffer);
packetBuffer.writeItem(recipe.output);
packetBuffer.writeVarInt(recipe.inputCount);
packetBuffer.writeVarInt(recipe.toolLevel);
packetBuffer.writeVarInt(recipe.anvilLevel);
packetBuffer.writeVarInt(recipe.damage);
}
}
}

View file

@ -3,37 +3,55 @@ package ru.bclib.registry;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import ru.bclib.BCLib;
import net.minecraft.world.level.block.Block;
import ru.bclib.config.PathConfig;
import java.util.List;
import java.util.Map;
public abstract class BaseRegistry<T> {
private static final List<BaseRegistry<?>> REGISTRIES = Lists.newArrayList();
private static final Map<String, List<Item>> MOD_BLOCKS = Maps.newHashMap();
private static final Map<String, List<Item>> MOD_BLOCK_ITEMS = Maps.newHashMap();
private static final Map<String, List<Block>> MOD_BLOCKS = Maps.newHashMap();
private static final Map<String, List<Item>> MOD_ITEMS = Maps.newHashMap();
protected final CreativeModeTab creativeTab;
protected final PathConfig config;
protected BaseRegistry(CreativeModeTab creativeTab, PathConfig config) {
this.creativeTab = creativeTab;
this.config = config;
REGISTRIES.add(this);
}
public abstract T register(ResourceLocation objId, T obj);
public abstract void registerItem(ResourceLocation id, Item item);
public FabricItemSettings makeItemSettings() {
FabricItemSettings properties = new FabricItemSettings();
return (FabricItemSettings) properties.tab(creativeTab);
}
private void registerInternal() {}
public static Map<String, List<Item>> getRegisteredBlocks() {
return MOD_BLOCKS;
return MOD_BLOCK_ITEMS;
}
public static Map<String, List<Item>> getRegisteredItems() {
return MOD_ITEMS;
}
public static List<Item> getModBlocks(String modId) {
if (MOD_BLOCKS.containsKey(modId)) {
return MOD_BLOCKS.get(modId);
public static List<Item> getModBlockItems(String modId) {
if (MOD_BLOCK_ITEMS.containsKey(modId)) {
return MOD_BLOCK_ITEMS.get(modId);
}
List<Item> modBlocks = Lists.newArrayList();
MOD_BLOCKS.put(modId, modBlocks);
MOD_BLOCK_ITEMS.put(modId, modBlocks);
return modBlocks;
}
@ -46,36 +64,16 @@ public abstract class BaseRegistry<T> {
return modBlocks;
}
public static List<Block> getModBlocks(String modId) {
if (MOD_BLOCKS.containsKey(modId)) {
return MOD_BLOCKS.get(modId);
}
List<Block> modBlocks = Lists.newArrayList();
MOD_BLOCKS.put(modId, modBlocks);
return modBlocks;
}
public static void register() {
REGISTRIES.forEach(BaseRegistry::registerInternal);
}
protected BaseRegistry(CreativeModeTab creativeTab) {
this.creativeTab = creativeTab;
REGISTRIES.add(this);
}
public T register(String name, T obj) {
return register(createModId(name), obj);
}
public abstract T register(ResourceLocation objId, T obj);
public ResourceLocation createModId(String name) {
return BCLib.makeID(name);
}
public void registerItem(ResourceLocation id, Item item, List<Item> registry) {
if (item != Items.AIR) {
Registry.register(Registry.ITEM, id, item);
registry.add(item);
}
}
public FabricItemSettings makeItemSettings() {
FabricItemSettings properties = new FabricItemSettings();
return (FabricItemSettings) properties.tab(creativeTab);
}
private void registerInternal() {}
}

View file

@ -6,16 +6,25 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block;
import ru.bclib.api.TagAPI;
import ru.bclib.blocks.BaseLeavesBlock;
import ru.bclib.blocks.FeatureSaplingBlock;
import ru.bclib.config.PathConfig;
import ru.bclib.interfaces.CustomItemProvider;
public abstract class BlockRegistry extends BaseRegistry<Block> {
protected BlockRegistry(CreativeModeTab creativeTab) {
super(creativeTab);
public class BlockRegistry extends BaseRegistry<Block> {
public BlockRegistry(CreativeModeTab creativeTab, PathConfig config) {
super(creativeTab, config);
}
@Override
public Block register(ResourceLocation id, Block block) {
if (!config.getBooleanRoot(id.getNamespace(), true)) {
return block;
}
BlockItem item = null;
if (block instanceof CustomItemProvider) {
item = ((CustomItemProvider) block).getCustomItem(id, makeItemSettings());
@ -24,20 +33,46 @@ public abstract class BlockRegistry extends BaseRegistry<Block> {
item = new BlockItem(block, makeItemSettings());
}
registerBlockItem(id, item);
if (block.defaultBlockState().getMaterial().isFlammable() && FlammableBlockRegistry.getDefaultInstance()
.get(block)
.getBurnChance() == 0) {
if (block.defaultBlockState().getMaterial().isFlammable() && FlammableBlockRegistry.getDefaultInstance().get(block).getBurnChance() == 0) {
FlammableBlockRegistry.getDefaultInstance().add(block, 5, 5);
}
return Registry.register(Registry.BLOCK, id, block);
block = Registry.register(Registry.BLOCK, id, block);
getModBlocks(id.getNamespace()).add(block);
if (block instanceof BaseLeavesBlock){
TagAPI.addTags(block, TagAPI.BLOCK_LEAVES);
if (item != null){
TagAPI.addTags(item, TagAPI.ITEM_LEAVES);
}
} else if (block instanceof FeatureSaplingBlock){
TagAPI.addTags(block, TagAPI.BLOCK_SAPLINGS);
if (item != null){
TagAPI.addTags(item, TagAPI.ITEM_SAPLINGS);
}
}
return block;
}
public Block registerBlockOnly(ResourceLocation id, Block block) {
if (!config.getBooleanRoot(id.getNamespace(), true)) {
return block;
}
getModBlocks(id.getNamespace()).add(block);
return Registry.register(Registry.BLOCK, id, block);
}
private Item registerBlockItem(ResourceLocation id, Item item) {
registerItem(id, item, BaseRegistry.getModBlocks(id.getNamespace()));
registerItem(id, item);
return item;
}
@Override
public void registerItem(ResourceLocation id, Item item) {
if (item != null && item != Items.AIR) {
Registry.register(Registry.ITEM, id, item);
getModBlockItems(id.getNamespace()).add(item);
}
}
}

View file

@ -3,7 +3,9 @@ package ru.bclib.registry;
import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags;
import net.minecraft.core.BlockSource;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.core.dispenser.ShearsDispenseItemBehavior;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.tags.Tag;
@ -15,12 +17,13 @@ import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.ShovelItem;
import net.minecraft.world.item.SpawnEggItem;
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.TieredItem;
import net.minecraft.world.level.block.DispenserBlock;
import ru.bclib.api.TagAPI;
import ru.bclib.config.PathConfig;
import ru.bclib.items.BaseDiscItem;
import ru.bclib.items.BaseDrinkItem;
import ru.bclib.items.BaseSpawnEggItem;
@ -28,30 +31,44 @@ import ru.bclib.items.ModelProviderItem;
import ru.bclib.items.tool.BaseAxeItem;
import ru.bclib.items.tool.BaseHoeItem;
import ru.bclib.items.tool.BasePickaxeItem;
import ru.bclib.items.tool.BaseShearsItem;
public abstract class ItemRegistry extends BaseRegistry<Item> {
protected ItemRegistry(CreativeModeTab creativeTab) {
super(creativeTab);
public class ItemRegistry extends BaseRegistry<Item> {
public ItemRegistry(CreativeModeTab creativeTab, PathConfig config) {
super(creativeTab, config);
}
public Item registerDisc(String name, int power, SoundEvent sound) {
return register(name, new BaseDiscItem(power, sound, makeItemSettings().stacksTo(1)));
public Item registerDisc(ResourceLocation itemId, int power, SoundEvent sound) {
BaseDiscItem item = new BaseDiscItem(power, sound, makeItemSettings().stacksTo(1));
if (!config.getBoolean("musicDiscs", itemId.getPath(), true)) {
return item;
}
return register(itemId, new BaseDiscItem(power, sound, makeItemSettings().stacksTo(1)));
}
public Item registerItem(String name) {
return register(name, new ModelProviderItem(makeItemSettings()));
public Item register(ResourceLocation itemId) {
return register(itemId, new ModelProviderItem(makeItemSettings()));
}
@Override
public Item register(ResourceLocation itemId, Item item) {
registerItem(itemId, item, BaseRegistry.getModItems(itemId.getNamespace()));
if (!config.getBoolean("items", itemId.getPath(), true)) {
return item;
}
registerItem(itemId, item);
return item;
}
public TieredItem registerTool(String name, TieredItem item) {
ResourceLocation id = createModId(name);
registerItem(id, item, BaseRegistry.getModItems(id.getNamespace()));
public Item registerTool(ResourceLocation itemId, Item item) {
if (!config.getBoolean("tools", itemId.getPath(), true)) {
return item;
}
registerItem(itemId, item);
if (item instanceof ShovelItem) {
TagAPI.addTag((Tag.Named<Item>) FabricToolTags.SHOVELS, item);
@ -68,12 +85,21 @@ public abstract class ItemRegistry extends BaseRegistry<Item> {
else if (item instanceof BaseHoeItem) {
TagAPI.addTag((Tag.Named<Item>) FabricToolTags.HOES, item);
}
else if (item instanceof BaseShearsItem) {
TagAPI.addTags(item, (Tag.Named<Item>) FabricToolTags.SHEARS, TagAPI.ITEM_SHEARS, TagAPI.ITEM_COMMON_SHEARS);
DispenserBlock.registerBehavior(item.asItem(), new ShearsDispenseItemBehavior());
}
return item;
}
public Item registerEgg(String name, EntityType<? extends Mob> type, int background, int dots) {
public Item registerEgg(ResourceLocation itemId, EntityType<? extends Mob> type, int background, int dots) {
SpawnEggItem item = new BaseSpawnEggItem(type, background, dots, makeItemSettings());
if (!config.getBoolean("spawnEggs", itemId.getPath(), true)) {
return item;
}
DefaultDispenseItemBehavior behavior = new DefaultDispenseItemBehavior() {
public ItemStack execute(BlockSource pointer, ItemStack stack) {
Direction direction = pointer.getBlockState().getValue(DispenserBlock.FACING);
@ -92,31 +118,42 @@ public abstract class ItemRegistry extends BaseRegistry<Item> {
}
};
DispenserBlock.registerBehavior(item, behavior);
return register(name, item);
return register(itemId, item);
}
public Item registerFood(String name, int hunger, float saturation, MobEffectInstance... effects) {
public Item registerFood(ResourceLocation itemId, int hunger, float saturation, MobEffectInstance... effects) {
FoodProperties.Builder builder = new FoodProperties.Builder().nutrition(hunger).saturationMod(saturation);
for (MobEffectInstance effect : effects) {
builder.effect(effect, 1F);
}
return registerFood(name, builder.build());
return registerFood(itemId, builder.build());
}
public Item registerFood(String name, FoodProperties foodComponent) {
return register(name, new ModelProviderItem(makeItemSettings().food(foodComponent)));
public Item registerFood(ResourceLocation itemId, FoodProperties foodComponent) {
return register(itemId, new ModelProviderItem(makeItemSettings().food(foodComponent)));
}
public Item registerDrink(String name) {
return register(name, new BaseDrinkItem(makeItemSettings().stacksTo(1)));
public Item registerDrink(ResourceLocation itemId, FoodProperties foodComponent) {
return register(itemId, new BaseDrinkItem(makeItemSettings().stacksTo(1).food(foodComponent)));
}
public Item registerDrink(String name, FoodProperties foodComponent) {
return register(name, new BaseDrinkItem(makeItemSettings().stacksTo(1).food(foodComponent)));
}
public Item registerDrink(String name, int hunger, float saturation) {
public Item registerDrink(ResourceLocation itemId, int hunger, float saturation) {
FoodProperties.Builder builder = new FoodProperties.Builder().nutrition(hunger).saturationMod(saturation);
return registerDrink(name, builder.build());
return registerDrink(itemId, builder.build());
}
@Override
public void registerItem(ResourceLocation id, Item item) {
if (item != null && item != Items.AIR) {
Registry.register(Registry.ITEM, id, item);
getModItems(id.getNamespace()).add(item);
}
}
public Item register(ResourceLocation itemId, Item item, String category) {
if (config.getBoolean(category, itemId.getPath(), true)) {
registerItem(itemId, item);
}
return item;
}
}

View file

@ -0,0 +1,74 @@
package ru.bclib.util;
import com.google.gson.JsonObject;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.bclib.BCLib;
public class ItemUtil {
public static String toStackString(@NotNull ItemStack stack) {
try {
if (stack == null) {
throw new IllegalStateException("Stack can't be null!");
}
Item item = stack.getItem();
return Registry.ITEM.getKey(item) + ":" + stack.getCount();
}
catch (Exception ex) {
BCLib.LOGGER.error("ItemStack serialization error!", ex);
}
return "";
}
@Nullable
public static ItemStack fromStackString(String stackString) {
if (stackString == null || stackString.equals("")) {
return null;
}
try {
String[] parts = stackString.split(":");
if (parts.length < 2) return null;
if (parts.length == 2) {
ResourceLocation itemId = new ResourceLocation(stackString);
Item item = Registry.ITEM.getOptional(itemId).orElseThrow(() -> {
return new IllegalStateException("Output item " + itemId + " does not exists!");
});
return new ItemStack(item);
}
ResourceLocation itemId = new ResourceLocation(parts[0], parts[1]);
Item item = Registry.ITEM.getOptional(itemId).orElseThrow(() -> {
return new IllegalStateException("Output item " + itemId + " does not exists!");
});
return new ItemStack(item, Integer.valueOf(parts[2]));
}
catch (Exception ex) {
BCLib.LOGGER.error("ItemStack deserialization error!", ex);
}
return null;
}
@Nullable
public static ItemStack fromJsonRecipe(JsonObject recipe) {
try {
if (!recipe.has("item")) {
throw new IllegalStateException("Invalid JsonObject. Entry 'item' does not exists!");
}
ResourceLocation itemId = new ResourceLocation(GsonHelper.getAsString(recipe, "item"));
Item item = Registry.ITEM.getOptional(itemId).orElseThrow(() -> {
return new IllegalStateException("Output item " + itemId + " does not exists!");
});
int count = GsonHelper.getAsInt(recipe, "count", 1);
return new ItemStack(item, count);
}
catch (Exception ex) {
BCLib.LOGGER.error("ItemStack deserialization error!", ex);
}
return null;
}
}

View file

@ -1,22 +1,35 @@
package ru.bclib.util;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.SemanticVersion;
import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.VersionParsingException;
import net.fabricmc.loader.api.metadata.ContactInformation;
import net.fabricmc.loader.api.metadata.CustomValue;
import net.fabricmc.loader.api.metadata.ModDependency;
import net.fabricmc.loader.api.metadata.ModEnvironment;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.impl.metadata.ModMetadataParser;
import net.fabricmc.loader.impl.metadata.ParseMetadataException;
import net.fabricmc.loader.api.metadata.Person;
import net.fabricmc.loader.util.version.SemanticVersionImpl;
import org.apache.logging.log4j.LogManager;
import ru.bclib.BCLib;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
@ -59,17 +72,19 @@ public class ModUtil {
Path modMetaFile = fs.getPath("fabric.mod.json");
if (modMetaFile != null) {
try (InputStream is = Files.newInputStream(modMetaFile)) {
ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
mods.put(mc.getId(), new ModInfo(mc, file));
//ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
ModMetadata mc = readJSON(is, uri.toString());
if (mc!=null){
mods.put(mc.getId(), new ModInfo(mc, file));
}
}
}
} catch (ParseMetadataException e) {
} catch (Exception e) {
BCLib.LOGGER.error(e.getMessage());
}
}
}
catch (IOException e) {
catch (Exception e) {
BCLib.LOGGER.error(e.getMessage());
}
}));
@ -77,6 +92,176 @@ public class ModUtil {
return mods;
}
private static ModMetadata readJSON(InputStream is, String sourceFile) throws IOException {
try (com.google.gson.stream.JsonReader reader = new JsonReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
JsonObject data = new JsonParser().parse(reader).getAsJsonObject();
Version ver;
try {
ver = new SemanticVersionImpl(data.get("version").getAsString(), false);
}
catch (VersionParsingException e) {
BCLib.LOGGER.error("Unable to parse Version in " + sourceFile);
return null;
}
if (data.get("id") == null){
BCLib.LOGGER.error("Unable to read ID in " + sourceFile);
return null;
}
if (data.get("name") == null){
BCLib.LOGGER.error("Unable to read name in " + sourceFile);
return null;
}
return new ModMetadata() {
@Override
public Version getVersion() {
return ver;
}
@Override
public String getType() {
return "fabric";
}
@Override
public String getId() {
return data.get("id").getAsString();
}
@Override
public Collection<String> getProvides() {
return new ArrayList<>();
}
@Override
public ModEnvironment getEnvironment() {
JsonElement env = data.get("environment");
if (env==null) {
BCLib.LOGGER.error("No environment specified in " + sourceFile);
return ModEnvironment.UNIVERSAL;
}
final String environment = env.getAsString().toLowerCase(Locale.ROOT);
if (environment.isEmpty() || environment.equals("*") || environment.equals("common")) {
JsonElement entrypoints = data.get("entrypoints");
boolean hasClient = true;
//check if there is an actual client entrypoint
if (entrypoints!=null && entrypoints.isJsonObject()){
JsonElement client = entrypoints.getAsJsonObject().get("client");
if (client!=null && client.isJsonArray()){
hasClient = client.getAsJsonArray().size() > 0;
} else if (client==null || !client.isJsonPrimitive()){
hasClient = false;
} else if (!client.getAsJsonPrimitive().isString()){
hasClient = false;
}
}
if (hasClient == false) return ModEnvironment.SERVER;
return ModEnvironment.UNIVERSAL;
} else if (environment.equals("client")) {
return ModEnvironment.CLIENT;
} else if (environment.equals("server")) {
return ModEnvironment.SERVER;
} else {
BCLib.LOGGER.error("Unable to read environment in " + sourceFile);
return ModEnvironment.UNIVERSAL;
}
}
@Override
public Collection<ModDependency> getDepends() {
return new ArrayList<>();
}
@Override
public Collection<ModDependency> getRecommends() {
return new ArrayList<>();
}
@Override
public Collection<ModDependency> getSuggests() {
return new ArrayList<>();
}
@Override
public Collection<ModDependency> getConflicts() {
return new ArrayList<>();
}
@Override
public Collection<ModDependency> getBreaks() {
return new ArrayList<>();
}
public Collection<ModDependency> getDependencies() {
return new ArrayList<>();
}
@Override
public String getName() {
return data.get("name").getAsString();
}
@Override
public String getDescription() {
return "";
}
@Override
public Collection<Person> getAuthors() {
return new ArrayList<>();
}
@Override
public Collection<Person> getContributors() {
return new ArrayList<>();
}
@Override
public ContactInformation getContact() {
return null;
}
@Override
public Collection<String> getLicense() {
return new ArrayList<>();
}
@Override
public Optional<String> getIconPath(int size) {
return Optional.empty();
}
@Override
public boolean containsCustomValue(String key) {
return false;
}
@Override
public CustomValue getCustomValue(String key) {
return null;
}
@Override
public Map<String, CustomValue> getCustomValues() {
return new HashMap<>();
}
@Override
public boolean containsCustomElement(String key) {
return false;
}
public JsonElement getCustomElement(String key) {
return null;
}
};
}
}
/**
* Returns the {@link ModInfo} or {@code null} if the mod was not found.
* <p>

View file

@ -0,0 +1,25 @@
package ru.bclib.util;
import net.minecraft.core.Registry;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
public class RecipeHelper {
public static boolean exists(ItemLike item) {
if (item instanceof Block) {
return Registry.BLOCK.getKey((Block) item) != Registry.BLOCK.getDefaultKey();
}
else {
return Registry.ITEM.getKey(item.asItem()) != Registry.ITEM.getDefaultKey();
}
}
public static boolean exists(ItemLike... items) {
for (ItemLike item : items) {
if (!exists(item)) {
return false;
}
}
return true;
}
}

View file

@ -30,7 +30,7 @@ public class TranslationHelper {
Gson gson = new Gson();
InputStream inputStream = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/" + languageCode + ".json");
JsonObject translation = gson.fromJson(new InputStreamReader(inputStream), JsonObject.class);
JsonObject translation = inputStream == null ? new JsonObject() : gson.fromJson(new InputStreamReader(inputStream), JsonObject.class);
Registry.BLOCK.forEach(block -> {
if (Registry.BLOCK.getKey(block).getNamespace().equals(modID)) {

View file

@ -70,6 +70,7 @@ public class BCLBiomeDef {
private int waterFogColor = 329011;
private int waterColor = 4159204;
private int fogColor = 10518688;
private int skyColor = 0;
private float fogDensity = 1F;
private float depth = 0.1F;
@ -247,11 +248,24 @@ public class BCLBiomeDef {
return ColorUtil.color(r, g, b);
}
public BCLBiomeDef setFogColor(int r, int g, int b) {
this.fogColor = getColor(r, g, b);
public BCLBiomeDef setSkyColor(int rgb) {
this.skyColor = rgb;
return this;
}
public BCLBiomeDef setSkyColor(int r, int g, int b) {
return setSkyColor(getColor(r, g, b));
}
public BCLBiomeDef setFogColor(int rgb) {
this.fogColor = rgb;
return this;
}
public BCLBiomeDef setFogColor(int r, int g, int b) {
return setFogColor(getColor(r, g, b));
}
public BCLBiomeDef setFogDensity(float density) {
this.fogDensity = density;
return this;
@ -333,7 +347,7 @@ public class BCLBiomeDef {
addCustomToBuild(generationSettings);
effects.skyColor(0)
effects.skyColor(skyColor)
.waterColor(waterColor)
.waterFogColor(waterFogColor)
.fogColor(fogColor)
@ -380,9 +394,9 @@ public class BCLBiomeDef {
ConfiguredFeature<?, ?> feature;
}
private static final class CarverInfo {
private static final class CarverInfo <C extends CarverConfiguration> {
Carving carverStep;
ConfiguredWorldCarver<CarverConfiguration> carver;
ConfiguredWorldCarver<C> carver;
}
public ResourceLocation getID() {
@ -401,7 +415,7 @@ public class BCLBiomeDef {
return edgeSize;
}
public BCLBiomeDef addCarver(Carving carverStep, ConfiguredWorldCarver<CarverConfiguration> carver) {
public <C extends CarverConfiguration> BCLBiomeDef addCarver(Carving carverStep, ConfiguredWorldCarver<C> carver) {
CarverInfo info = new CarverInfo();
info.carverStep = carverStep;
info.carver = carver;

View file

@ -29,6 +29,7 @@ public class BCLibNetherBiomeSource extends BiomeSource {
private BiomeMap biomeMap;
private final long seed;
@Deprecated(forRemoval = true)
public static final List<Consumer<BCLibNetherBiomeSource>> onInit = new LinkedList<>();
public BCLibNetherBiomeSource(Registry<Biome> biomeRegistry, long seed) {

View file

@ -12,11 +12,15 @@ public class GeneratorOptions {
private static int biomeSizeEndVoid;
private static Function<Point, Boolean> endLandFunction;
private static boolean farEndBiomes = true;
private static boolean customNetherBiomeSource = true;
private static boolean customEndBiomeSource = true;
public static void init() {
biomeSizeNether = Configs.GENERATOR_CONFIG.getInt("nether.biomeMap", "biomeSize", 256);
biomeSizeEndLand = Configs.GENERATOR_CONFIG.getInt("end.biomeMap", "biomeSizeLand", 256);
biomeSizeEndVoid = Configs.GENERATOR_CONFIG.getInt("end.biomeMap", "biomeSizeVoid", 256);
customNetherBiomeSource = Configs.GENERATOR_CONFIG.getBoolean("options", "customNetherBiomeSource", true);
customEndBiomeSource = Configs.GENERATOR_CONFIG.getBoolean("options", "customEndBiomeSource", true);
}
public static int getBiomeSizeNether() {
@ -46,4 +50,12 @@ public class GeneratorOptions {
public static void setFarEndBiomes(boolean farEndBiomes) {
GeneratorOptions.farEndBiomes = farEndBiomes;
}
public static boolean customNetherBiomeSource() {
return customNetherBiomeSource;
}
public static boolean customEndBiomeSource() {
return customEndBiomeSource;
}
}