diff --git a/src/main/java/org/betterx/bclib/BCLib.java b/src/main/java/org/betterx/bclib/BCLib.java new file mode 100644 index 00000000..5e4505b0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/BCLib.java @@ -0,0 +1,65 @@ +package org.betterx.bclib; + +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.loader.api.FabricLoader; + +import org.betterx.bclib.api.WorldDataAPI; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.api.dataexchange.handler.autosync.*; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.recipes.AnvilRecipe; +import org.betterx.bclib.recipes.CraftingRecipes; +import org.betterx.bclib.registry.BaseBlockEntities; +import org.betterx.bclib.registry.BaseRegistry; +import org.betterx.bclib.util.Logger; +import org.betterx.bclib.world.generator.BCLibEndBiomeSource; +import org.betterx.bclib.world.generator.BCLibNetherBiomeSource; +import org.betterx.bclib.world.generator.GeneratorOptions; + +import java.util.List; + +public class BCLib implements ModInitializer { + public static final String MOD_ID = "bclib"; + public static final Logger LOGGER = new Logger(MOD_ID); + + @Override + public void onInitialize() { + BaseRegistry.register(); + GeneratorOptions.init(); + BaseBlockEntities.register(); + BCLibEndBiomeSource.register(); + BCLibNetherBiomeSource.register(); + TagAPI.init(); + CraftingRecipes.init(); + WorldDataAPI.registerModCache(MOD_ID); + DataExchangeAPI.registerMod(MOD_ID); + AnvilRecipe.register(); + + DataExchangeAPI.registerDescriptors(List.of( + HelloClient.DESCRIPTOR, + HelloServer.DESCRIPTOR, + RequestFiles.DESCRIPTOR, + SendFiles.DESCRIPTOR, + Chunker.DESCRIPTOR + )); + + BCLibPatch.register(); + Configs.save(); + } + + public static boolean isDevEnvironment() { + return FabricLoader.getInstance().isDevelopmentEnvironment(); + } + + public static boolean isClient() { + return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT; + } + + public static ResourceLocation makeID(String path) { + return new ResourceLocation(MOD_ID, path); + } +} diff --git a/src/main/java/org/betterx/bclib/BCLibPatch.java b/src/main/java/org/betterx/bclib/BCLibPatch.java new file mode 100644 index 00000000..8e28cf25 --- /dev/null +++ b/src/main/java/org/betterx/bclib/BCLibPatch.java @@ -0,0 +1,95 @@ +package org.betterx.bclib; + +import net.minecraft.nbt.CompoundTag; + +import org.betterx.bclib.api.datafixer.DataFixerAPI; +import org.betterx.bclib.api.datafixer.ForcedLevelPatch; +import org.betterx.bclib.api.datafixer.MigrationProfile; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.world.generator.GeneratorOptions; + +public final class BCLibPatch { + public static void register() { + // TODO separate values in config on client side (config screen) + if (Configs.MAIN_CONFIG.repairBiomes() && (GeneratorOptions.fixEndBiomeSource() || GeneratorOptions.fixNetherBiomeSource())) { + DataFixerAPI.registerPatch(BiomeSourcePatch::new); + } + } +} + +final class BiomeSourcePatch extends ForcedLevelPatch { + private static final String NETHER_BIOME_SOURCE = "bclib:nether_biome_source"; + private static final String END_BIOME_SOURCE = "bclib:end_biome_source"; + private static final String MC_NETHER = "minecraft:the_nether"; + private static final String MC_END = "minecraft:the_end"; + + protected BiomeSourcePatch() { + super(BCLib.MOD_ID, "1.2.1"); + } + + @Override + protected Boolean runLevelDatPatch(CompoundTag root, MigrationProfile profile) { + CompoundTag worldGenSettings = root.getCompound("Data").getCompound("WorldGenSettings"); + CompoundTag dimensions = worldGenSettings.getCompound("dimensions"); + long seed = worldGenSettings.getLong("seed"); + boolean result = false; + + if (GeneratorOptions.fixNetherBiomeSource()) { + if (!dimensions.contains(MC_NETHER) || !isBCLibEntry(dimensions.getCompound(MC_NETHER))) { + CompoundTag dimRoot = new CompoundTag(); + dimRoot.put("generator", makeNetherGenerator(seed)); + dimRoot.putString("type", MC_NETHER); + dimensions.put(MC_NETHER, dimRoot); + result = true; + } + } + + if (GeneratorOptions.fixEndBiomeSource()) { + if (!dimensions.contains(MC_END) || !isBCLibEntry(dimensions.getCompound(MC_END))) { + CompoundTag dimRoot = new CompoundTag(); + dimRoot.put("generator", makeEndGenerator(seed)); + dimRoot.putString("type", MC_END); + dimensions.put(MC_END, dimRoot); + result = true; + } + } + + return result; + } + + private boolean isBCLibEntry(CompoundTag dimRoot) { + String type = dimRoot.getCompound("generator").getCompound("biome_source").getString("type"); + if (type.isEmpty() || type.length() < 5) { + return false; + } + return type.startsWith("bclib"); + } + + public static CompoundTag makeNetherGenerator(long seed) { + CompoundTag generator = new CompoundTag(); + generator.putString("type", "minecraft:noise"); + generator.putString("settings", "minecraft:nether"); + generator.putLong("seed", seed); + + CompoundTag biomeSource = new CompoundTag(); + biomeSource.putString("type", NETHER_BIOME_SOURCE); + biomeSource.putLong("seed", seed); + generator.put("biome_source", biomeSource); + + return generator; + } + + public static CompoundTag makeEndGenerator(long seed) { + CompoundTag generator = new CompoundTag(); + generator.putString("type", "minecraft:noise"); + generator.putString("settings", "minecraft:end"); + generator.putLong("seed", seed); + + CompoundTag biomeSource = new CompoundTag(); + biomeSource.putString("type", END_BIOME_SOURCE); + biomeSource.putLong("seed", seed); + generator.put("biome_source", biomeSource); + + return generator; + } +} diff --git a/src/main/java/org/betterx/bclib/api/BonemealAPI.java b/src/main/java/org/betterx/bclib/api/BonemealAPI.java new file mode 100644 index 00000000..6441d5e7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/BonemealAPI.java @@ -0,0 +1,142 @@ +package org.betterx.bclib.api; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.Block; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.betterx.bclib.util.WeightedList; + +import java.util.Map; +import java.util.Set; + +public class BonemealAPI { + private static final Map>> WATER_GRASS_BIOMES = Maps.newHashMap(); + private static final Map>> LAND_GRASS_BIOMES = Maps.newHashMap(); + private static final Map> WATER_GRASS_TYPES = Maps.newHashMap(); + private static final Map> LAND_GRASS_TYPES = Maps.newHashMap(); + private static final Map SPREADABLE_BLOCKS = Maps.newHashMap(); + private static final Set TERRAIN_TO_SPREAD = Sets.newHashSet(); + private static final Set TERRAIN = Sets.newHashSet(); + + public static void addSpreadableBlock(Block spreadableBlock, Block surfaceForSpread) { + SPREADABLE_BLOCKS.put(spreadableBlock, surfaceForSpread); + TERRAIN_TO_SPREAD.add(surfaceForSpread); + TERRAIN.add(surfaceForSpread); + } + + public static boolean isTerrain(Block block) { + return TERRAIN.contains(block); + } + + public static boolean isSpreadableTerrain(Block block) { + return TERRAIN_TO_SPREAD.contains(block); + } + + public static Block getSpreadable(Block block) { + return SPREADABLE_BLOCKS.get(block); + } + + public static void addLandGrass(Block plant, Block... terrain) { + for (Block block : terrain) { + addLandGrass(plant, block, 1F); + } + } + + public static void addLandGrass(ResourceLocation biome, Block plant, Block... terrain) { + for (Block block : terrain) { + addLandGrass(biome, plant, block, 1F); + } + } + + public static void addLandGrass(Block plant, Block terrain, float chance) { + WeightedList list = LAND_GRASS_TYPES.get(terrain); + if (list == null) { + list = new WeightedList(); + LAND_GRASS_TYPES.put(terrain, list); + } + TERRAIN.add(terrain); + list.add(plant, chance); + } + + public static void addLandGrass(ResourceLocation biome, Block plant, Block terrain, float chance) { + Map> map = LAND_GRASS_BIOMES.get(biome); + if (map == null) { + map = Maps.newHashMap(); + LAND_GRASS_BIOMES.put(biome, map); + } + WeightedList list = map.get(terrain); + if (list == null) { + list = new WeightedList(); + map.put(terrain, list); + } + TERRAIN.add(terrain); + list.add(plant, chance); + } + + public static void addWaterGrass(Block plant, Block... terrain) { + for (Block block : terrain) { + addWaterGrass(plant, block, 1F); + } + } + + public static void addWaterGrass(ResourceLocation biome, Block plant, Block... terrain) { + for (Block block : terrain) { + addWaterGrass(biome, plant, block, 1F); + } + } + + public static void addWaterGrass(Block plant, Block terrain, float chance) { + WeightedList list = WATER_GRASS_TYPES.get(terrain); + if (list == null) { + list = new WeightedList(); + WATER_GRASS_TYPES.put(terrain, list); + } + TERRAIN.add(terrain); + list.add(plant, chance); + } + + public static void addWaterGrass(ResourceLocation biome, Block plant, Block terrain, float chance) { + Map> map = WATER_GRASS_BIOMES.get(biome); + if (map == null) { + map = Maps.newHashMap(); + WATER_GRASS_BIOMES.put(biome, map); + } + WeightedList list = map.get(terrain); + if (list == null) { + list = new WeightedList(); + map.put(terrain, list); + } + TERRAIN.add(terrain); + list.add(plant, chance); + } + + public static Block getLandGrass(ResourceLocation biomeID, Block terrain, RandomSource random) { + Map> map = LAND_GRASS_BIOMES.get(biomeID); + WeightedList list = null; + if (map != null) { + list = map.get(terrain); + if (list == null) { + list = LAND_GRASS_TYPES.get(terrain); + } + } else { + list = LAND_GRASS_TYPES.get(terrain); + } + return list == null ? null : list.get(random); + } + + public static Block getWaterGrass(ResourceLocation biomeID, Block terrain, RandomSource random) { + Map> map = WATER_GRASS_BIOMES.get(biomeID); + WeightedList list = null; + if (map != null) { + list = map.get(terrain); + if (list == null) { + list = WATER_GRASS_TYPES.get(terrain); + } + } else { + list = WATER_GRASS_TYPES.get(terrain); + } + return list == null ? null : list.get(random); + } +} diff --git a/src/main/java/org/betterx/bclib/api/ComposterAPI.java b/src/main/java/org/betterx/bclib/api/ComposterAPI.java new file mode 100644 index 00000000..bb76f9e1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/ComposterAPI.java @@ -0,0 +1,23 @@ +package org.betterx.bclib.api; + +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; + +import org.betterx.bclib.mixin.common.ComposterBlockAccessor; + +public class ComposterAPI { + public static Block allowCompost(float chance, Block block) { + if (block != null) { + allowCompost(chance, block.asItem()); + } + return block; + } + + public static Item allowCompost(float chance, Item item) { + if (item != null && item != Items.AIR) { + ComposterBlockAccessor.callAdd(chance, item); + } + return item; + } +} diff --git a/src/main/java/org/betterx/bclib/api/LifeCycleAPI.java b/src/main/java/org/betterx/bclib/api/LifeCycleAPI.java new file mode 100644 index 00000000..9d868941 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/LifeCycleAPI.java @@ -0,0 +1,141 @@ +package org.betterx.bclib.api; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.world.level.CustomSpawner; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.ServerLevelData; + +import org.betterx.bclib.api.datafixer.DataFixerAPI; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * provides some lifetime hooks for a Minecraft instance + */ +public class LifeCycleAPI { + private final static List onLoadLevelBiomes = new ArrayList<>(2); + private final static List onLoadLevel = new ArrayList<>(2); + private final static List beforeLoadLevel = new ArrayList<>(2); + + /** + * A callback function that is used for each new ServerLevel instance + */ + public interface BeforeLevelLoadCall { + void beforeLoad(); + } + + /** + * A callback function that is used for each new ServerLevel instance + */ + public interface LevelLoadBiomesCall { + void onLoad(ServerLevel world, long seed, Registry registry); + } + + /** + * A callback function that is used for each new ServerLevel instance + */ + public interface LevelLoadCall { + void onLoad( + ServerLevel world, + MinecraftServer minecraftServer, + Executor executor, + LevelStorageSource.LevelStorageAccess levelStorageAccess, + ServerLevelData serverLevelData, + ResourceKey resourceKey, + ChunkProgressListener chunkProgressListener, + boolean bl, + long l, + List list, + boolean bl2); + } + + /** + * Register a callback that is called before a level is loaded or created, + * but after the {@link WorldDataAPI} was initialized and patches from + * the {@link DataFixerAPI} were applied. + * + * @param call The callback Method + */ + public static void beforeLevelLoad(BeforeLevelLoadCall call) { + beforeLoadLevel.add(call); + } + + /** + * Register a callback that is called when a new {@code ServerLevel is instantiated}. + * This callback will receive the world seed as well as it's biome registry. + * + * @param call The calbback Method + */ + public static void onLevelLoad(LevelLoadBiomesCall call) { + onLoadLevelBiomes.add(call); + } + + /** + * Register a callback that is called when a new {@code ServerLevel is instantiated}. + * This callbacl will receiv all parameters that were passed to the ServerLevel's constructor + * + * @param call The calbback Method + */ + public static void onLevelLoad(LevelLoadCall call) { + onLoadLevel.add(call); + } + + /** + * For internal use, You should not call this method! + */ + public static void _runBeforeLevelLoad() { + beforeLoadLevel.forEach(c -> c.beforeLoad()); + } + + /** + * For internal use, You should not call this method! + * + * @param minecraftServer + * @param executor + * @param levelStorageAccess + * @param serverLevelData + * @param resourceKey + * @param chunkProgressListener + * @param bl + * @param l + * @param list + * @param bl2 + */ + public static void _runLevelLoad(ServerLevel world, + MinecraftServer minecraftServer, + Executor executor, + LevelStorageSource.LevelStorageAccess levelStorageAccess, + ServerLevelData serverLevelData, + ResourceKey resourceKey, + ChunkProgressListener chunkProgressListener, + boolean bl, + long l, + List list, + boolean bl2) { + onLoadLevel.forEach(c -> c.onLoad( + world, + minecraftServer, + executor, + levelStorageAccess, + serverLevelData, + resourceKey, + chunkProgressListener, + bl, + l, + list, + bl2) + ); + + final long seed = world.getSeed(); + final Registry biomeRegistry = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY); + onLoadLevelBiomes.forEach(c -> c.onLoad(world, seed, biomeRegistry)); + } +} diff --git a/src/main/java/org/betterx/bclib/api/ModIntegrationAPI.java b/src/main/java/org/betterx/bclib/api/ModIntegrationAPI.java new file mode 100644 index 00000000..66a1aff5 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/ModIntegrationAPI.java @@ -0,0 +1,48 @@ +package org.betterx.bclib.api; + +import net.fabricmc.loader.api.FabricLoader; + +import com.google.common.collect.Lists; +import org.betterx.bclib.integration.ModIntegration; + +import java.util.List; + +public class ModIntegrationAPI { + private static final List INTEGRATIONS = Lists.newArrayList(); + private static final boolean HAS_CANVAS = FabricLoader.getInstance().isModLoaded("canvas"); + + /** + * Registers mod integration + * + * @param integration + * @return + */ + public static ModIntegration register(ModIntegration integration) { + INTEGRATIONS.add(integration); + return integration; + } + + /** + * Get all registered mod integrations. + * + * @return {@link List} of {@link ModIntegration}. + */ + public static List getIntegrations() { + return INTEGRATIONS; + } + + /** + * Initialize all integrations, only for internal usage. + */ + public static void registerAll() { + INTEGRATIONS.forEach(integration -> { + if (integration.modIsInstalled()) { + integration.init(); + } + }); + } + + public static boolean hasCanvas() { + return HAS_CANVAS; + } +} diff --git a/src/main/java/org/betterx/bclib/api/PostInitAPI.java b/src/main/java/org/betterx/bclib/api/PostInitAPI.java new file mode 100644 index 00000000..7d9d3ce4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/PostInitAPI.java @@ -0,0 +1,148 @@ +package org.betterx.bclib.api; + +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.Registry; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; + +import com.google.common.collect.Lists; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.api.tag.NamedMineableTags; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.blocks.BaseBarrelBlock; +import org.betterx.bclib.blocks.BaseChestBlock; +import org.betterx.bclib.blocks.BaseFurnaceBlock; +import org.betterx.bclib.blocks.BaseSignBlock; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.client.render.BaseChestBlockEntityRenderer; +import org.betterx.bclib.client.render.BaseSignBlockEntityRenderer; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.interfaces.PostInitable; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.interfaces.TagProvider; +import org.betterx.bclib.interfaces.tools.*; +import org.betterx.bclib.registry.BaseBlockEntities; + +import java.util.List; +import java.util.function.Consumer; + +public class PostInitAPI { + private static List> postInitFunctions = Lists.newArrayList(); + private static List> blockTags = Lists.newArrayList(); + private static List> itemTags = Lists.newArrayList(); + + /** + * Register a new function which will be called after all mods are initiated. Will be called on both client and server. + * + * @param function {@link Consumer} with {@code boolean} parameter ({@code true} for client, {@code false} for server). + */ + public static void register(Consumer function) { + postInitFunctions.add(function); + } + + /** + * Called in proper BCLib entry points, for internal usage only. + * + * @param isClient {@code boolean}, {@code true} for client, {@code false} for server. + */ + public static void postInit(boolean isClient) { + if (postInitFunctions == null) { + return; + } + postInitFunctions.forEach(function -> function.accept(isClient)); + Registry.BLOCK.forEach(block -> { + processBlockCommon(block); + if (isClient) { + processBlockClient(block); + } + }); + + + Registry.ITEM.forEach(item -> { + processItemCommon(item); + }); + postInitFunctions = null; + blockTags = null; + itemTags = null; + BiomeAPI.loadFabricAPIBiomes(); + Configs.BIOMES_CONFIG.saveChanges(); + } + + @Environment(EnvType.CLIENT) + private static void processBlockClient(Block block) { + if (block instanceof RenderLayerProvider) { + BCLRenderLayer layer = ((RenderLayerProvider) block).getRenderLayer(); + if (layer == BCLRenderLayer.CUTOUT) BlockRenderLayerMap.INSTANCE.putBlock(block, RenderType.cutout()); + else if (layer == BCLRenderLayer.TRANSLUCENT) + BlockRenderLayerMap.INSTANCE.putBlock(block, RenderType.translucent()); + } + if (block instanceof BaseChestBlock) { + BaseChestBlockEntityRenderer.registerRenderLayer(block); + } else if (block instanceof BaseSignBlock) { + BaseSignBlockEntityRenderer.registerRenderLayer(block); + } + } + + private static void processItemCommon(Item item) { + if (item instanceof TagProvider provider) { + try { + provider.addTags(null, itemTags); + } catch (NullPointerException ex) { + BCLib.LOGGER.error(item + " probably tried to access blockTags.", ex); + } + itemTags.forEach(tag -> TagAPI.addItemTag(tag, item)); + itemTags.clear(); + } + } + + private static void processBlockCommon(Block block) { + if (block instanceof PostInitable) { + ((PostInitable) block).postInit(); + } + if (block instanceof BaseChestBlock) { + BaseBlockEntities.CHEST.registerBlock(block); + } else if (block instanceof BaseSignBlock) { + BaseBlockEntities.SIGN.registerBlock(block); + } else if (block instanceof BaseBarrelBlock) { + BaseBlockEntities.BARREL.registerBlock(block); + } else if (block instanceof BaseFurnaceBlock) { + BaseBlockEntities.FURNACE.registerBlock(block); + } + if (!(block instanceof PreventMineableAdd)) { + if (block instanceof AddMineableShears) { + TagAPI.addBlockTags(block, NamedMineableTags.SHEARS); + } + if (block instanceof AddMineableAxe) { + TagAPI.addBlockTags(block, NamedMineableTags.AXE); + } + if (block instanceof AddMineablePickaxe) { + TagAPI.addBlockTags(block, NamedMineableTags.PICKAXE); + } + if (block instanceof AddMineableShovel) { + TagAPI.addBlockTags(block, NamedMineableTags.SHOVEL); + } + if (block instanceof AddMineableHoe) { + TagAPI.addBlockTags(block, NamedMineableTags.HOE); + } + if (block instanceof AddMineableSword) { + TagAPI.addBlockTags(block, NamedMineableTags.SWORD); + } + if (block instanceof AddMineableHammer) { + TagAPI.addBlockTags(block, NamedMineableTags.HAMMER); + } + } + if (block instanceof TagProvider) { + ((TagProvider) block).addTags(blockTags, itemTags); + blockTags.forEach(tag -> TagAPI.addBlockTag(tag, block)); + itemTags.forEach(tag -> TagAPI.addItemTag(tag, block)); + blockTags.clear(); + itemTags.clear(); + } + } +} diff --git a/src/main/java/org/betterx/bclib/api/ShovelAPI.java b/src/main/java/org/betterx/bclib/api/ShovelAPI.java new file mode 100644 index 00000000..b56b5a01 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/ShovelAPI.java @@ -0,0 +1,22 @@ +package org.betterx.bclib.api; + +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.mixin.common.ShovelItemAccessor; + +import java.util.Map; + +public class ShovelAPI { + /** + * Will add left-click behaviour to shovel: when it is targeting cetrain {@link Block} it will be converting to new + * {@link BlockState} on usage. Example: grass converting to path. + * + * @param target {@link Block} that will be converted. + * @param convert {@link BlockState} to convert block into. + */ + public static void addShovelBehaviour(Block target, BlockState convert) { + Map map = ShovelItemAccessor.bclib_getFlattenables(); + map.put(target, convert); + } +} diff --git a/src/main/java/org/betterx/bclib/api/WorldDataAPI.java b/src/main/java/org/betterx/bclib/api/WorldDataAPI.java new file mode 100644 index 00000000..0cad8947 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/WorldDataAPI.java @@ -0,0 +1,146 @@ +package org.betterx.bclib.api; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.datafixer.DataFixerAPI; +import org.betterx.bclib.util.ModUtil; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + +/** + * Mod-specifix data-storage for a world. + *

+ * This class provides the ability for mod to store persistent data inside a world. The Storage for the world is + * currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)} + * or {@link DataFixerAPI#initializeWorldData(File, boolean)} + */ +public class WorldDataAPI { + private static final Map TAGS = Maps.newHashMap(); + private static final List MODS = Lists.newArrayList(); + private static File dataDir; + + public static void load(File dataDir) { + WorldDataAPI.dataDir = dataDir; + MODS.stream() + .parallel() + .forEach(modID -> { + File file = new File(dataDir, modID + ".nbt"); + CompoundTag root = new CompoundTag(); + if (file.exists()) { + try { + root = NbtIo.readCompressed(file); + } catch (IOException e) { + BCLib.LOGGER.error("World data loading failed", e); + } + TAGS.put(modID, root); + } else { + Optional optional = FabricLoader.getInstance() + .getModContainer(modID); + if (optional.isPresent()) { + ModContainer modContainer = optional.get(); + if (BCLib.isDevEnvironment()) { + root.putString("version", "255.255.9999"); + } else { + root.putString("version", modContainer.getMetadata() + .getVersion() + .toString()); + } + TAGS.put(modID, root); + saveFile(modID); + } + } + }); + } + + /** + * Register mod cache, world cache is located in world data folder. + * + * @param modID - {@link String} modID. + */ + public static void registerModCache(String modID) { + MODS.add(modID); + } + + /** + * Get root {@link CompoundTag} for mod cache in world data folder. + * + * @param modID - {@link String} modID. + * @return {@link CompoundTag} + */ + public static CompoundTag getRootTag(String modID) { + CompoundTag root = TAGS.get(modID); + if (root == null) { + root = new CompoundTag(); + TAGS.put(modID, root); + } + return root; + } + + /** + * Get {@link CompoundTag} with specified path from mod cache in world data folder. + * + * @param modID - {@link String} path to tag, dot-separated. + * @return {@link CompoundTag} + */ + public static CompoundTag getCompoundTag(String modID, String path) { + String[] parts = path.split("\\."); + CompoundTag tag = getRootTag(modID); + for (String part : parts) { + if (tag.contains(part)) { + tag = tag.getCompound(part); + } else { + CompoundTag t = new CompoundTag(); + tag.put(part, t); + tag = t; + } + } + return tag; + } + + /** + * Forces mod cache file to be saved. + * + * @param modID {@link String} mod ID. + */ + public static void saveFile(String modID) { + try { + if (!dataDir.exists()) { + dataDir.mkdirs(); + } + NbtIo.writeCompressed(getRootTag(modID), new File(dataDir, modID + ".nbt")); + } catch (IOException e) { + BCLib.LOGGER.error("World data saving failed", e); + } + } + + /** + * Get stored mod version (only for mods with registered cache). + * + * @return {@link String} mod version. + */ + public static String getModVersion(String modID) { + return getRootTag(modID).getString("version"); + } + + /** + * Get stored mod version as integer (only for mods with registered cache). + * + * @return {@code int} mod version. + */ + public static int getIntModVersion(String modID) { + return ModUtil.convertModVersion(getModVersion(modID)); + } +} diff --git a/src/main/java/org/betterx/bclib/api/biomes/BCLBiomeBuilder.java b/src/main/java/org/betterx/bclib/api/biomes/BCLBiomeBuilder.java new file mode 100644 index 00000000..1445447c --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/biomes/BCLBiomeBuilder.java @@ -0,0 +1,806 @@ +package org.betterx.bclib.api.biomes; + +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.data.worldgen.BiomeDefaultFeatures; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.Music; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.tags.TagKey; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.biome.Biome.BiomeBuilder; +import net.minecraft.world.level.biome.Biome.Precipitation; +import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.Noises; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; + +import net.fabricmc.fabric.api.biome.v1.BiomeModifications; + +import com.google.common.collect.Lists; +import org.betterx.bclib.api.surface.SurfaceRuleBuilder; +import org.betterx.bclib.entity.BCLEntityWrapper; +import org.betterx.bclib.mixin.common.BiomeGenerationSettingsAccessor; +import org.betterx.bclib.util.CollectionsUtil; +import org.betterx.bclib.util.ColorUtil; +import org.betterx.bclib.util.Pair; +import org.betterx.bclib.util.TriFunction; +import org.betterx.bclib.world.biomes.BCLBiome; +import org.betterx.bclib.world.biomes.BCLBiomeSettings; +import org.betterx.bclib.world.features.BCLFeature; +import org.betterx.bclib.world.structures.BCLStructure; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +public class BCLBiomeBuilder { + @FunctionalInterface + public interface BiomeSupplier extends TriFunction { + } + + private static final BCLBiomeBuilder INSTANCE = new BCLBiomeBuilder(); + private static final SurfaceRules.ConditionSource SURFACE_NOISE = SurfaceRules.noiseCondition(Noises.SOUL_SAND_LAYER, + -0.012); + + private final List> structureTags = new ArrayList<>(8); + private final List>>> carvers = new ArrayList<>(1); + private BiomeGenerationSettings.Builder generationSettings; + private BiomeSpecialEffects.Builder effectsBuilder; + private MobSpawnSettings.Builder spawnSettings; + private SurfaceRules.RuleSource surfaceRule; + private Precipitation precipitation; + private ResourceLocation biomeID; + + private final List parameters = Lists.newArrayList(); + + //BiomeTags.IS_NETHER + private float temperature; + private float fogDensity; + private float genChance; + private float downfall; + private float height; + private int edgeSize; + private BCLBiome edge; + private boolean vertical; + + + /** + * Starts new biome building process. + * + * @param biomeID {@link ResourceLocation} biome identifier. + * @return prepared {@link BCLBiomeBuilder} instance. + */ + public static BCLBiomeBuilder start(ResourceLocation biomeID) { + INSTANCE.biomeID = biomeID; + INSTANCE.precipitation = Precipitation.NONE; + INSTANCE.generationSettings = null; + INSTANCE.effectsBuilder = null; + INSTANCE.spawnSettings = null; + INSTANCE.structureTags.clear(); + INSTANCE.temperature = 1.0F; + INSTANCE.fogDensity = 1.0F; + INSTANCE.edgeSize = 0; + INSTANCE.downfall = 1.0F; + INSTANCE.genChance = 1.0F; + INSTANCE.height = 0.1F; + INSTANCE.vertical = false; + INSTANCE.edge = null; + INSTANCE.carvers.clear(); + INSTANCE.parameters.clear(); + return INSTANCE; + } + + public BCLBiomeBuilder addNetherClimateParamater(float temperature, float humidity) { + parameters.add(Climate.parameters(temperature, humidity, 0, 0, 0, 0, 0)); + return this; + } + + /** + * Set biome {@link Precipitation}. Affect biome visual effects (rain, snow, none). + * + * @param precipitation {@link Precipitation} + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder precipitation(Precipitation precipitation) { + this.precipitation = precipitation; + return this; + } + + /** + * Set biome temperature, affect plant color, biome generation and ice formation. + * + * @param temperature biome temperature. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder temperature(float temperature) { + this.temperature = temperature; + return this; + } + + /** + * Set biome wetness (same as downfall). Affect plant color and biome generation. + * + * @param wetness biome wetness (downfall). + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder wetness(float wetness) { + this.downfall = wetness; + return this; + } + + /** + * Adds mob spawning to biome. + * + * @param entityType {@link EntityType} mob type. + * @param weight spawn weight. + * @param minGroupCount minimum mobs in group. + * @param maxGroupCount maximum mobs in group. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder spawn(EntityType entityType, + int weight, + int minGroupCount, + int maxGroupCount) { + getSpawns().addSpawn(entityType.getCategory(), + new SpawnerData(entityType, weight, minGroupCount, maxGroupCount)); + return this; + } + + /** + * Adds mob spawning to biome. + * + * @param wrapper {@link BCLEntityWrapper} mob type. + * @param weight spawn weight. + * @param minGroupCount minimum mobs in group. + * @param maxGroupCount maximum mobs in group. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder spawn(BCLEntityWrapper wrapper, + int weight, + int minGroupCount, + int maxGroupCount) { + if (wrapper.canSpawn()) { + return spawn(wrapper.type(), weight, minGroupCount, maxGroupCount); + } + + return this; + } + + /** + * Adds ambient particles to thr biome. + * + * @param particle {@link ParticleOptions} particles (or {@link net.minecraft.core.particles.ParticleType}). + * @param probability particle spawn probability, should have low value (example: 0.01F). + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder particles(ParticleOptions particle, float probability) { + getEffects().ambientParticle(new AmbientParticleSettings(particle, probability)); + return this; + } + + /** + * Sets sky color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder skyColor(int color) { + getEffects().skyColor(color); + return this; + } + + /** + * Sets sky color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder skyColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return skyColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets fog color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder fogColor(int color) { + getEffects().fogColor(color); + return this; + } + + /** + * Sets fog color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder fogColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return fogColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets fog density for the biome. + * + * @param density fog density as a float, default value is 1.0F. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder fogDensity(float density) { + this.fogDensity = density; + return this; + } + + /** + * Sets generation chance for this biome. + * + * @param genChance + * @return same {@link BCLBiomeBuilder}. + */ + public BCLBiomeBuilder genChance(float genChance) { + this.genChance = genChance; + return this; + } + + /** + * Sets edge size for this biome. + * + * @param edgeSize size of the Edge (in Blocks) + * @return same {@link BCLBiomeBuilder}. + */ + public BCLBiomeBuilder edgeSize(int edgeSize) { + this.edgeSize = edgeSize; + return this; + } + + /** + * Sets edge-Biome for this biome. + * + * @param edge The Edge Biome + * @return same {@link BCLBiomeBuilder}. + */ + public BCLBiomeBuilder edge(BCLBiome edge) { + this.edge = edge; + return this; + } + + + /** + * Sets edge-Biome for this biome. + * + * @param edge The Edge Biome + * @param edgeSize size of the Edge (in Blocks) + * @return same {@link BCLBiomeBuilder}. + */ + public BCLBiomeBuilder edge(BCLBiome edge, int edgeSize) { + this.edge(edge); + this.edgeSize(edgeSize); + return this; + } + + /** + * Sets water color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder waterColor(int color) { + getEffects().waterColor(color); + return this; + } + + /** + * Sets water color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder waterColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return waterColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets underwater fog color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder waterFogColor(int color) { + getEffects().waterFogColor(color); + return this; + } + + /** + * Sets underwater fog color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder waterFogColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return waterFogColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets water and underwater fig color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder waterAndFogColor(int color) { + return waterColor(color).waterFogColor(color); + } + + /** + * Sets water and underwater fig color for the biome. Color is in ARGB int format. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder waterAndFogColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return waterAndFogColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets grass color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder grassColor(int color) { + getEffects().grassColorOverride(color); + return this; + } + + /** + * Sets grass color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder grassColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return grassColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets leaves and plants color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder foliageColor(int color) { + getEffects().foliageColorOverride(color); + return this; + } + + /** + * Sets leaves and plants color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder foliageColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return foliageColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets grass, leaves and all plants color for the biome. Color is in ARGB int format. + * + * @param color ARGB color as integer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder plantsColor(int color) { + return grassColor(color).foliageColor(color); + } + + /** + * Sets grass, leaves and all plants color for the biome. Color represented as red, green and blue channel values. + * + * @param red red color component [0-255] + * @param green green color component [0-255] + * @param blue blue color component [0-255] + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder plantsColor(int red, int green, int blue) { + red = Mth.clamp(red, 0, 255); + green = Mth.clamp(green, 0, 255); + blue = Mth.clamp(blue, 0, 255); + return plantsColor(ColorUtil.color(red, green, blue)); + } + + /** + * Sets biome music, used for biomes in the Nether and End. + * + * @param music {@link Music} to use. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder music(Music music) { + getEffects().backgroundMusic(music); + return this; + } + + /** + * Sets biome music, used for biomes in the Nether and End. + * + * @param music {@link SoundEvent} to use. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder music(SoundEvent music) { + return music(new Music(music, 600, 2400, true)); + } + + /** + * Sets biome ambient loop sound. Can be used for biome environment. + * + * @param loopSound {@link SoundEvent} to use as a loop. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder loop(SoundEvent loopSound) { + getEffects().ambientLoopSound(loopSound); + return this; + } + + /** + * Sets biome mood sound. Can be used for biome environment. + * + * @param mood {@link SoundEvent} to use as a mood. + * @param tickDelay delay between sound events in ticks. + * @param blockSearchExtent block search radius (for area available for sound). + * @param soundPositionOffset offset in sound. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder mood(SoundEvent mood, int tickDelay, int blockSearchExtent, float soundPositionOffset) { + getEffects().ambientMoodSound(new AmbientMoodSettings(mood, tickDelay, blockSearchExtent, soundPositionOffset)); + return this; + } + + /** + * Sets biome mood sound. Can be used for biome environment. + * + * @param mood {@link SoundEvent} to use as a mood. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder mood(SoundEvent mood) { + return mood(mood, 6000, 8, 2.0F); + } + + /** + * Sets biome additionsl ambient sounds. + * + * @param additions {@link SoundEvent} to use. + * @param intensity sound intensity. Default is 0.0111F. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder additions(SoundEvent additions, float intensity) { + getEffects().ambientAdditionsSound(new AmbientAdditionsSettings(additions, intensity)); + return this; + } + + /** + * Sets biome additionsl ambient sounds. + * + * @param additions {@link SoundEvent} to use. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder additions(SoundEvent additions) { + return additions(additions, 0.0111F); + } + + /** + * Adds new feature to the biome. + * + * @param decoration {@link Decoration} feature step. + * @param feature {@link PlacedFeature}. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder feature(Decoration decoration, Holder feature) { + getGeneration().addFeature(decoration, feature); + return this; + } + + /** + * Adds vanilla Mushrooms. + * + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder defaultMushrooms() { + return feature(BiomeDefaultFeatures::addDefaultMushrooms); + } + + /** + * Adds vanilla Nether Ores. + * + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder netherDefaultOres() { + return feature(BiomeDefaultFeatures::addNetherDefaultOres); + } + + /** + * Will add features into biome, used for vanilla feature adding functions. + * + * @param featureAdd {@link Consumer} with {@link BiomeGenerationSettings.Builder}. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder feature(Consumer featureAdd) { + featureAdd.accept(getGeneration()); + return this; + } + + /** + * Adds new feature to the biome. + * + * @param feature {@link BCLFeature}. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder feature(BCLFeature feature) { + return feature(feature.getDecoration(), feature.getPlacedFeature()); + } + + /** + * Adds new structure feature into the biome. + * + * @param structureTag {@link TagKey} to add. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder structure(TagKey structureTag) { + structureTags.add(structureTag); + return this; + } + + /** + * Adds new structure feature into thr biome. Will add building biome into the structure list. + * + * @param structure {@link BCLStructure} to add. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder structure(BCLStructure structure) { + structure.addInternalBiome(biomeID); + return structure(structure.biomeTag); + } + + /** + * Adds new world carver into the biome. + * + * @param carver {@link ConfiguredWorldCarver} to add. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder carver(GenerationStep.Carving step, Holder> carver) { + final ResourceLocation immutableID = biomeID; + var oKey = carver.unwrapKey(); + if (oKey.isPresent()) { + BiomeModifications.addCarver(ctx -> ctx.getBiomeKey().location().equals(immutableID), + step, + (ResourceKey>) oKey.get()); + } + //carvers.add(new Pair<>(step, carver)); + return this; + } + + /** + * Adds new world surface rule for the given block + * + * @param surfaceBlock {@link Block} to use. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder surface(Block surfaceBlock) { + return surface(surfaceBlock.defaultBlockState()); + } + + /** + * Adds new world surface rule for the given block + * + * @param surfaceBlock {@link BlockState} to use. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder surface(BlockState surfaceBlock) { + return surface(SurfaceRuleBuilder.start().surface(surfaceBlock).build()); + } + + /** + * Adds blocks to the biome surface and below it (with specified depth). + * + * @param surfaceBlock {@link Block} that will cover biome. + * @param subterrainBlock {@link Block} below it with specified depth. + * @param depth thickness of bottom block layer. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder surface(Block surfaceBlock, Block subterrainBlock, int depth) { + return surface(SurfaceRuleBuilder + .start() + .surface(surfaceBlock.defaultBlockState()) + .subsurface(subterrainBlock.defaultBlockState(), depth) + .build()); + } + + /** + * Adds surface rule to this biome. + * + * @param newSurfaceRule {link SurfaceRules.RuleSource} surface rule. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder surface(SurfaceRules.RuleSource newSurfaceRule) { + this.surfaceRule = newSurfaceRule; + return this; + } + + /** + * Set terrain height for the biome. Can be used in custom generators, doesn't change vanilla biome distribution or generation. + * + * @param height a relative float terrain height value. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder terrainHeight(float height) { + this.height = height; + return this; + } + + + /** + * Make this a vertical Biome + * + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder vertical() { + this.vertical = vertical; + return this; + } + + /** + * Finalize biome creation. + * + * @return created {@link BCLBiome} instance. + */ + public BCLBiome build() { + return build((BiomeSupplier) BCLBiome::new); + } + + /** + * Finalize biome creation. + * + * @param biomeConstructor {@link BiFunction} biome constructor. + * @return created {@link BCLBiome} instance. + */ + @Deprecated(forRemoval = true) + public T build(BiFunction biomeConstructor) { + return build((id, biome, settings) -> biomeConstructor.apply(id, biome)); + } + + private static BiomeGenerationSettings fixGenerationSettings(BiomeGenerationSettings settings) { + //Fabric Biome Modification API can not handle an empty carver map, thus we will create one with + //an empty HolderSet for every possible step: + //https://github.com/FabricMC/fabric/issues/2079 + //TODO: Remove, once fabric gets fixed + if (settings instanceof BiomeGenerationSettingsAccessor acc) { + Map>> carvers = CollectionsUtil.getMutable(acc.bclib_getCarvers()); + for (GenerationStep.Carving step : GenerationStep.Carving.values()) { + carvers.computeIfAbsent(step, __ -> HolderSet.direct(Lists.newArrayList())); + } + acc.bclib_setCarvers(carvers); + } + return settings; + } + + /** + * Finalize biome creation. + * + * @param biomeConstructor {@link BiomeSupplier} biome constructor. + * @return created {@link BCLBiome} instance. + */ + public T build(BiomeSupplier biomeConstructor) { + BiomeBuilder builder = new BiomeBuilder() + .precipitation(precipitation) + .temperature(temperature) + .downfall(downfall); + + builder.mobSpawnSettings(getSpawns().build()); + builder.specialEffects(getEffects().build()); + + builder.generationSettings(fixGenerationSettings(getGeneration().build())); + + BCLBiomeSettings settings = BCLBiomeSettings.createBCL() + .setTerrainHeight(height) + .setFogDensity(fogDensity) + .setGenChance(genChance) + .setEdgeSize(edgeSize) + .setEdge(edge) + .setVertical(vertical) + .build(); + + final Biome biome = builder.build(); + final T res = biomeConstructor.apply(biomeID, biome, settings); + res.attachStructures(structureTags); + res.setSurface(surfaceRule); + res.addClimateParameters(parameters); + + //carvers.forEach(cfg -> BiomeAPI.addBiomeCarver(biome, cfg.second, cfg.first)); + return res; + } + + /** + * Get or create {@link BiomeSpecialEffects.Builder} for biome visual effects. + * For internal usage only. + * For internal usage only. + * + * @return new or same {@link BiomeSpecialEffects.Builder} instance. + */ + private BiomeSpecialEffects.Builder getEffects() { + if (effectsBuilder == null) { + effectsBuilder = new BiomeSpecialEffects.Builder(); + } + return effectsBuilder; + } + + /** + * Get or create {@link MobSpawnSettings.Builder} for biome mob spawning. + * For internal usage only. + * + * @return new or same {@link MobSpawnSettings.Builder} instance. + */ + private MobSpawnSettings.Builder getSpawns() { + if (spawnSettings == null) { + spawnSettings = new MobSpawnSettings.Builder(); + } + return spawnSettings; + } + + /** + * Get or create {@link BiomeGenerationSettings.Builder} for biome features and generation. + * For internal usage only. + * + * @return new or same {@link BiomeGenerationSettings.Builder} instance. + */ + private BiomeGenerationSettings.Builder getGeneration() { + if (generationSettings == null) { + generationSettings = new BiomeGenerationSettings.Builder(); + } + return generationSettings; + } +} diff --git a/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java b/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java new file mode 100644 index 00000000..4f2c91b3 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java @@ -0,0 +1,1049 @@ +package org.betterx.bclib.api.biomes; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BiomeTags; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeGenerationSettings; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.levelgen.GenerationStep.Carving; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraft.world.level.levelgen.structure.Structure; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.impl.biome.NetherBiomeData; +import net.fabricmc.fabric.impl.biome.TheEndBiomeData; + +import com.google.common.base.Suppliers; +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 org.apache.commons.lang3.mutable.MutableInt; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.tag.CommonBiomeTags; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.entity.BCLEntityWrapper; +import org.betterx.bclib.interfaces.*; +import org.betterx.bclib.mixin.client.MinecraftMixin; +import org.betterx.bclib.mixin.common.BiomeGenerationSettingsAccessor; +import org.betterx.bclib.mixin.common.MobSpawnSettingsAccessor; +import org.betterx.bclib.mixin.common.NoiseGeneratorSettingsMixin; +import org.betterx.bclib.util.CollectionsUtil; +import org.betterx.bclib.world.biomes.BCLBiome; +import org.betterx.bclib.world.biomes.FabricBiomesData; +import org.betterx.bclib.world.biomes.VanillaBiomeSettings; +import org.betterx.bclib.world.features.BCLFeature; + +import java.util.*; +import java.util.Map.Entry; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.jetbrains.annotations.Nullable; + +public class BiomeAPI { + public static class Dimension { + public static final Dimension NONE = new Dimension(); + public static final Dimension OVERWORLD = new Dimension(); + public static final Dimension NETHER = new Dimension(); + public static final Dimension END = new Dimension(); + public static final Dimension END_LAND = new Dimension(END); + public static final Dimension END_VOID = new Dimension(END); + + private static final Map DIMENSION_MAP = Maps.newHashMap(); + public final Dimension parentOrNull; + + public Dimension() { + this(null); + } + + public Dimension(Dimension parentOrNull) { + this.parentOrNull = parentOrNull; + } + + public boolean is(Dimension d) { + if (d == this) return true; + if (parentOrNull != null) return parentOrNull.is(d); + return false; + } + } + + /** + * Empty biome used as default value if requested biome doesn't exist or linked. Shouldn't be registered anywhere to prevent bugs. + * Have {@code Biomes.THE_VOID} as the reference biome. + */ + public static final BCLBiome EMPTY_BIOME = new BCLBiome(Biomes.THE_VOID.location()); + + private static final Map ID_MAP = Maps.newHashMap(); + private static final Map CLIENT = Maps.newHashMap(); + public static Registry biomeRegistry; + + private static final Map, Integer> FEATURE_ORDER = Maps.newHashMap(); + private static final MutableInt FEATURE_ORDER_ID = new MutableInt(0); + + private static final Map>>> MODIFICATIONS = Maps.newHashMap(); + private static final Map>>> TAG_ADDERS = Maps.newHashMap(); + private static final Map SURFACE_RULES = Maps.newHashMap(); + private static final Set MODIFIED_SURFACE_PROVIDERS = new HashSet<>(8); + + public static final BCLBiome NETHER_WASTES_BIOME = registerNetherBiome(getFromRegistry(Biomes.NETHER_WASTES).value()); + public static final BCLBiome CRIMSON_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.CRIMSON_FOREST).value()); + public static final BCLBiome WARPED_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.WARPED_FOREST).value()); + public static final BCLBiome SOUL_SAND_VALLEY_BIOME = registerNetherBiome(getFromRegistry(Biomes.SOUL_SAND_VALLEY).value()); + public static final BCLBiome BASALT_DELTAS_BIOME = registerNetherBiome(getFromRegistry(Biomes.BASALT_DELTAS).value()); + + public static final BCLBiome THE_END = registerEndLandBiome(getFromRegistry(Biomes.THE_END)); + public static final BCLBiome END_MIDLANDS = registerSubBiome(THE_END, + getFromRegistry(Biomes.END_MIDLANDS).value(), + 0.5F); + public static final BCLBiome END_HIGHLANDS = registerSubBiome(THE_END, + getFromRegistry(Biomes.END_HIGHLANDS).value(), + 0.5F); + + public static final BCLBiome END_BARRENS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("end_barrens"))); + public static final BCLBiome SMALL_END_ISLANDS = registerEndVoidBiome(getFromRegistry(new ResourceLocation( + "small_end_islands"))); + + private static void initFeatureOrder() { + if (!FEATURE_ORDER.isEmpty()) { + return; + } + + BuiltinRegistries.BIOME + .entrySet() + .stream() + .filter(entry -> entry + .getKey() + .location() + .getNamespace() + .equals("minecraft")) + .map(Entry::getValue) + .map(biome -> (BiomeGenerationSettingsAccessor) biome.getGenerationSettings()) + .map(BiomeGenerationSettingsAccessor::bclib_getFeatures) + .forEach(stepFeatureSuppliers -> stepFeatureSuppliers.forEach(step -> step.forEach(feature -> { + FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement()); + }))); + } + + /** + * Initialize registry for current server. + * + * @param biomeRegistry - {@link Registry} for {@link Biome}. + */ + public static void initRegistry(Registry biomeRegistry) { + if (biomeRegistry != BiomeAPI.biomeRegistry) { + BiomeAPI.biomeRegistry = biomeRegistry; + CLIENT.clear(); + } + } + + /** + * For internal use only. + *

+ * This method gets called before a world is loaded/created to flush cashes we build. The Method is + * called from {@link MinecraftMixin} + */ + public static void prepareNewLevel() { + MODIFIED_SURFACE_PROVIDERS.forEach(p -> p.bclib_clearBiomeSources()); + MODIFIED_SURFACE_PROVIDERS.clear(); + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * + * @param bclbiome {@link BCLBiome} + * @param dim The Dimension fo rthis Biome + * @return {@link BCLBiome} + */ + public static BCLBiome registerBiome(BCLBiome bclbiome, Dimension dim) { + if (BuiltinRegistries.BIOME.get(bclbiome.getID()) == null) { + final Biome biome = bclbiome.getBiome(); + ResourceLocation loc = bclbiome.getID(); + Registry.register(BuiltinRegistries.BIOME, loc, biome); + } + ID_MAP.put(bclbiome.getID(), bclbiome); + Dimension.DIMENSION_MAP.put(bclbiome.getID(), dim); + + if (dim != null && dim.is(Dimension.NETHER)) { + TagAPI.addBiomeTag(BiomeTags.IS_NETHER, bclbiome.getBiome()); + TagAPI.addBiomeTag(CommonBiomeTags.IN_NETHER, bclbiome.getBiome()); + } else if (dim != null && dim.is(Dimension.END)) { + TagAPI.addBiomeTag(BiomeTags.IS_END, bclbiome.getBiome()); + } + + bclbiome.afterRegistration(); + + return bclbiome; + } + + public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome) { + final Dimension dim = Dimension.DIMENSION_MAP.getOrDefault(parent.getID(), Dimension.NONE); + registerBiome(subBiome, dim); + parent.addSubBiome(subBiome); + + + return subBiome; + } + + public static BCLBiome registerSubBiome(BCLBiome parent, Biome biome, float genChance) { + BCLBiome subBiome = new BCLBiome(biome, VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); + return registerSubBiome(parent, subBiome); + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API. + * + * @param bclBiome {@link BCLBiome} + * @return {@link BCLBiome} + */ + public static BCLBiome registerNetherBiome(BCLBiome bclBiome) { + registerBiome(bclBiome, Dimension.NETHER); + + ResourceKey key = BiomeAPI.getBiomeKeyOrThrow(bclBiome.getBiomeHolder()); + bclBiome.forEachClimateParameter(p -> NetherBiomeData.addNetherBiome(key, p)); + return bclBiome; + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API. + * + * @param biome {@link BCLBiome} + * @return {@link BCLBiome} + */ + public static BCLBiome registerNetherBiome(Biome biome) { + BCLBiome bclBiome = new BCLBiome(biome, null); + registerBiome(bclBiome, Dimension.NETHER); + return bclBiome; + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). + * + * @param biome {@link BCLBiome} + * @return {@link BCLBiome} + */ + public static BCLBiome registerEndLandBiome(BCLBiome biome) { + registerBiome(biome, Dimension.END_LAND); + + float weight = biome.getGenChance(); + ResourceKey key = BiomeAPI.getBiomeKey(biome.getBiome()); + TheEndBiomeData.addEndBiomeReplacement(Biomes.END_HIGHLANDS, key, weight); + TheEndBiomeData.addEndBiomeReplacement(Biomes.END_MIDLANDS, key, weight); + return biome; + } + + /** + * Register {@link BCLBiome} wrapper for {@link Biome}. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). + * + * @param biome {@link BCLBiome} + * @return {@link BCLBiome} + */ + public static BCLBiome registerEndLandBiome(Holder biome) { + BCLBiome bclBiome = new BCLBiome(biome.value(), null); + + registerBiome(bclBiome, Dimension.END_LAND); + return bclBiome; + } + + /** + * Register {@link BCLBiome} wrapper for {@link Biome}. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). + * + * @param biome {@link BCLBiome}; + * @param genChance float generation chance. + * @return {@link BCLBiome} + */ + public static BCLBiome registerEndLandBiome(Holder biome, float genChance) { + BCLBiome bclBiome = new BCLBiome(biome.value(), + VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); + + registerBiome(bclBiome, Dimension.END_LAND); + return bclBiome; + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). + * + * @param biome {@link BCLBiome} + * @return {@link BCLBiome} + */ + public static BCLBiome registerEndVoidBiome(BCLBiome biome) { + registerBiome(biome, Dimension.END_VOID); + + float weight = biome.getGenChance(); + ResourceKey key = BiomeAPI.getBiomeKeyOrThrow(biome.getBiomeHolder()); + TheEndBiomeData.addEndBiomeReplacement(Biomes.SMALL_END_ISLANDS, key, weight); + return biome; + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). + * + * @param biome {@link BCLBiome} + * @return {@link BCLBiome} + */ + public static BCLBiome registerEndVoidBiome(Holder biome) { + BCLBiome bclBiome = new BCLBiome(biome.value(), null); + + registerBiome(bclBiome, Dimension.END_VOID); + return bclBiome; + } + + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). + * + * @param biome {@link BCLBiome}. + * @param genChance float generation chance. + * @return {@link BCLBiome} + */ + public static BCLBiome registerEndVoidBiome(Holder biome, float genChance) { + BCLBiome bclBiome = new BCLBiome(biome.value(), + VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); + + registerBiome(bclBiome, Dimension.END_VOID); + return bclBiome; + } + + /** + * Get {@link BCLBiome} from {@link Biome} instance on server. Used to convert world biomes to BCLBiomes. + * + * @param biome - {@link Holder} from world. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + public static BCLBiome getFromBiome(Holder biome) { + if (biomeRegistry == null) { + return EMPTY_BIOME; + } + return ID_MAP.getOrDefault(biome.unwrapKey().orElseThrow().location(), EMPTY_BIOME); + } + + /** + * Get {@link BCLBiome} from biome on client. Used in fog rendering. + * + * @param biome - {@link Biome} from client world. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + @Environment(EnvType.CLIENT) + public static BCLBiome getRenderBiome(Biome biome) { + BCLBiome endBiome = CLIENT.get(biome); + if (endBiome == null) { + Minecraft minecraft = Minecraft.getInstance(); + ResourceLocation id = minecraft.level.registryAccess() + .registryOrThrow(Registry.BIOME_REGISTRY) + .getKey(biome); + endBiome = id == null ? EMPTY_BIOME : ID_MAP.getOrDefault(id, EMPTY_BIOME); + CLIENT.put(biome, endBiome); + } + return endBiome; + } + + /** + * Get biome {@link ResourceKey} from given {@link Biome}. + * + * @param biome - {@link Biome} from server world. + * @return biome {@link ResourceKey} or {@code null}. + */ + @Nullable + public static ResourceKey getBiomeKey(Biome biome) { + return BuiltinRegistries.BIOME + .getResourceKey(biome) + .orElseGet(() -> biomeRegistry != null ? biomeRegistry.getResourceKey(biome).orElse(null) : null); + } + + /** + * Get biome {@link ResourceLocation} from given {@link Biome}. + * + * @param biome - {@link Biome} from server world. + * @return biome {@link ResourceLocation}. + */ + public static ResourceLocation getBiomeID(Biome biome) { + ResourceLocation id = BuiltinRegistries.BIOME.getKey(biome); + if (id == null && biomeRegistry != null) { + id = biomeRegistry.getKey(biome); + } + return id == null ? EMPTY_BIOME.getID() : id; + } + + /** + * Get biome {@link ResourceLocation} from given {@link Biome}. + * + * @param biome - {@link Holder} from server world. + * @return biome {@link ResourceLocation}. + */ + public static ResourceLocation getBiomeID(Holder biome) { + var oKey = biome.unwrapKey(); + if (oKey.isPresent()) { + return oKey.get().location(); + } + return null; + } + + public static ResourceKey getBiomeKey(Holder biome) { + return biome.unwrapKey().orElse(null); + } + + public static ResourceKey getBiomeKeyOrThrow(Holder biome) { + return biome.unwrapKey().orElseThrow(); + } + + /** + * Get {@link BCLBiome} from given {@link ResourceLocation}. + * + * @param biomeID - biome {@link ResourceLocation}. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + public static BCLBiome getBiome(ResourceLocation biomeID) { + return ID_MAP.getOrDefault(biomeID, EMPTY_BIOME); + } + + /** + * Get {@link BCLBiome} from given {@link Biome}. + * + * @param biome - biome {@link Biome}. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + public static BCLBiome getBiome(Biome biome) { + return getBiome(BiomeAPI.getBiomeID(biome)); + } + + /** + * Get {@link BCLBiome} from given {@link Biome}. + * + * @param biome - biome {@link Biome}. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + public static BCLBiome getBiome(Holder biome) { + return getBiome(BiomeAPI.getBiomeID(biome)); + } + + /** + * Check if biome with {@link ResourceLocation} exists in API registry. + * + * @param biomeID - biome {@link ResourceLocation}. + * @return {@code true} if biome exists in API registry and {@code false} if not. + */ + public static boolean hasBiome(ResourceLocation biomeID) { + return ID_MAP.containsKey(biomeID); + } + + /** + * Load biomes from Fabric API. For internal usage only. + */ + public static void loadFabricAPIBiomes() { + FabricBiomesData.NETHER_BIOMES.forEach((key) -> { + if (!hasBiome(key.location())) { + registerNetherBiome(BuiltinRegistries.BIOME.get(key)); + } + }); + + FabricBiomesData.END_LAND_BIOMES.forEach((key, weight) -> { + if (!hasBiome(key.location())) { + registerEndLandBiome(BuiltinRegistries.BIOME.getHolder(key).orElseThrow(), weight); + } + }); + + FabricBiomesData.END_VOID_BIOMES.forEach((key, weight) -> { + if (!hasBiome(key.location())) { + registerEndVoidBiome(BuiltinRegistries.BIOME.getOrCreateHolder(key), weight); + } + }); + } + + @Nullable + public static Holder getFromRegistry(ResourceLocation key) { + return BuiltinRegistries.BIOME.getHolder(ResourceKey.create(Registry.BIOME_REGISTRY, key)).orElseThrow(); + } + + @Nullable + public static Holder getFromRegistry(ResourceKey key) { + return BuiltinRegistries.BIOME.getOrCreateHolder(key); + } + + public static boolean isDatapackBiome(ResourceLocation biomeID) { + return getFromRegistry(biomeID) == null; + } + + public static boolean wasRegisteredAs(ResourceLocation biomeID, Dimension dim) { + if (!Dimension.DIMENSION_MAP.containsKey(biomeID)) return false; + return Dimension.DIMENSION_MAP.get(biomeID).is(dim); + } + + public static boolean wasRegisteredAsNetherBiome(ResourceLocation biomeID) { + return wasRegisteredAs(biomeID, Dimension.NETHER); + } + + public static boolean wasRegisteredAsEndBiome(ResourceLocation biomeID) { + return wasRegisteredAs(biomeID, Dimension.END); + } + + public static boolean wasRegisteredAsEndLandBiome(ResourceLocation biomeID) { + return wasRegisteredAs(biomeID, Dimension.END_LAND); + } + + public static boolean wasRegisteredAsEndVoidBiome(ResourceLocation biomeID) { + return wasRegisteredAs(biomeID, Dimension.END_VOID); + } + + /** + * 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> modification) { + List>> modifications = MODIFICATIONS.computeIfAbsent(dimensionID, + k -> Lists.newArrayList()); + 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> 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> 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> modification) { + registerBiomeModification(Level.END, modification); + } + + /** + * For internal use only + */ + public static void _runTagAdders() { + for (var mod : TAG_ADDERS.entrySet()) { + Stream s = null; + if (mod.getKey() == Level.NETHER) s = Dimension.DIMENSION_MAP.entrySet() + .stream() + .filter(e -> e.getValue().is(Dimension.NETHER)) + .map(e -> e.getKey()); + else if (mod.getKey() == Level.END) s = Dimension.DIMENSION_MAP.entrySet() + .stream() + .filter(e -> e.getValue().is(Dimension.END)) + .map(e -> e.getKey()); + if (s != null) { + s.forEach(id -> { + BCLBiome b = BiomeAPI.getBiome(id); + Holder biomeHolder = b.getBiomeHolder(); + if (biomeHolder.isBound()) { + mod.getValue().forEach(c -> c.accept(id, biomeHolder)); + } + }); + } + } + } + + /** + * 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 onFinishingBiomeTags(ResourceKey dimensionID, + BiConsumer> modification) { + List>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID, + k -> Lists.newArrayList()); + modifications.add(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 onFinishingNetherBiomeTags(BiConsumer> modification) { + onFinishingBiomeTags(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 onFinishingEndBiomeTags(BiConsumer> modification) { + onFinishingBiomeTags(Level.END, modification); + } + + /** + * Will apply biome modiffications to world, internal usage only. + * + * @param level + */ + public static void applyModifications(ServerLevel level) { + NoiseGeneratorSettings noiseGeneratorSettings = null; + final ChunkGenerator chunkGenerator = level.getChunkSource().getGenerator(); + final BiomeSource source = chunkGenerator.getBiomeSource(); + final Set> biomes = source.possibleBiomes(); + + if (chunkGenerator instanceof NoiseGeneratorSettingsProvider gen) + noiseGeneratorSettings = gen.bclib_getNoiseGeneratorSettings(); + + // Datapacks (like Amplified Nether)will change the GeneratorSettings upon load, so we will + // only use the default Setting for Nether/End if we were unable to find a settings object + if (noiseGeneratorSettings == null) { + if (level.dimension() == Level.NETHER) { + noiseGeneratorSettings = BuiltinRegistries.NOISE_GENERATOR_SETTINGS.get(NoiseGeneratorSettings.NETHER); + } else if (level.dimension() == Level.END) { + noiseGeneratorSettings = BuiltinRegistries.NOISE_GENERATOR_SETTINGS.get(NoiseGeneratorSettings.END); + } + } + + List>> modifications = MODIFICATIONS.get(level.dimension()); + for (Holder biomeHolder : biomes) { + if (biomeHolder.isBound()) { + applyModificationsAndUpdateFeatures(modifications, biomeHolder); + } + } + + + if (noiseGeneratorSettings != null) { + final SurfaceRuleProvider provider = SurfaceRuleProvider.class.cast(noiseGeneratorSettings); + // Multiple Biomes can use the same generator. So we need to keep track of all Biomes that are + // Provided by all the BiomeSources that use the same generator. + // This happens for example when using the MiningDimensions, which reuses the generator for the + // Nethering Dimension + MODIFIED_SURFACE_PROVIDERS.add(provider); + provider.bclib_addBiomeSource(source); + } else { + BCLib.LOGGER.warning("No generator for " + source); + } + + ((BiomeSourceAccessor) source).bclRebuildFeatures(); + } + + private static void applyModificationsAndUpdateFeatures(List>> modifications, + Holder biome) { + ResourceLocation biomeID = getBiomeID(biome); + if (modifications != null) { + modifications.forEach(consumer -> { + consumer.accept(biomeID, biome); + }); + } + + sortBiomeFeatures(biome); + } + + /** + * Create a unique sort order for all Features of the Biome + * + * @param biome The {@link Biome} to sort the features for + */ + public static void sortBiomeFeatures(Holder biome) { + BiomeGenerationSettings settings = biome.value().getGenerationSettings(); + BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings; + List> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); + final int size = featureList.size(); + for (int i = 0; i < size; i++) { + List> features = getFeaturesListCopy(featureList, i); + sortFeatures(features); + featureList.set(i, HolderSet.direct(features)); + } + accessor.bclib_setFeatures(featureList); + } + + private static List getRuleSourcesForBiomes(Set> biomes) { + Set biomeIDs = biomes + .stream() + .map(biome -> getBiomeID(biome)) + .collect(Collectors.toSet()); + return getRuleSourcesFromIDs(biomeIDs); + } + + /** + * Creates a list of SurfaceRules for all Biomes that are managed by the passed {@link BiomeSource}. + * If we have Surface rules for any of the Biomes from the given set of {@link BiomeSource}, they + * will be added to the result + *

+ * Note: This Method is used in the {@link NoiseGeneratorSettingsMixin} which in turn + * is called from {@link #applyModifications(ServerLevel)}. + * + * @param sources The Set of {@link BiomeSource} we want to consider + * @return A list of {@link RuleSource}-Objects that are needed to create those Biomes + */ + public static List getRuleSources(Set sources) { + final Set> biomes = new HashSet<>(); + for (BiomeSource s : sources) { + biomes.addAll(s.possibleBiomes()); + } + + return getRuleSourcesForBiomes(biomes); + } + + private static List getRuleSourcesFromIDs(Set biomeIDs) { + List rules = Lists.newArrayList(); + SURFACE_RULES.forEach((biomeID, rule) -> { + if (biomeIDs.contains(biomeID)) { + rules.add(rule); + } + }); + return rules; + } + + /** + * Adds new features to existing biome. + * + * @param biome {@link Biome} to add features in. + * @param feature {@link ConfiguredFeature} to add. + */ + public static void addBiomeFeature(Holder biome, BCLFeature feature) { + addBiomeFeature(biome, feature.getDecoration(), feature.getPlacedFeature()); + } + + /** + * Adds new features to existing biome. + * + * @param biome {@link Biome} to add features in. + * @param step a {@link Decoration} step for the feature. + * @param featureList {@link ConfiguredFeature} to add. + */ + public static void addBiomeFeature(Holder biome, Decoration step, Holder... featureList) { + addBiomeFeature(biome, step, List.of(featureList)); + } + + /** + * Adds new features to existing biome. + * + * @param biome {@link Biome} to add features in. + * @param step a {@link Decoration} step for the feature. + * @param additionalFeatures List of {@link ConfiguredFeature} to add. + */ + private static void addBiomeFeature(Holder biome, + Decoration step, + List> additionalFeatures) { + BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) biome.value() + .getGenerationSettings(); + List> allFeatures = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); + List> features = getFeaturesListCopy(allFeatures, step); + + for (var feature : additionalFeatures) { + if (!features.contains(feature)) + features.add(feature); + } + + allFeatures.set(step.ordinal(), HolderSet.direct(features)); + final Supplier>> flowerFeatures = Suppliers.memoize(() -> allFeatures.stream() + .flatMap( + HolderSet::stream) + .map(Holder::value) + .flatMap( + PlacedFeature::getFeatures) + .filter(configuredFeature -> configuredFeature.feature() == Feature.FLOWER) + .collect( + ImmutableList.toImmutableList())); + final Supplier> featureSet = Suppliers.memoize(() -> allFeatures.stream() + .flatMap(HolderSet::stream) + .map(Holder::value) + .collect(Collectors.toSet())); + + accessor.bclib_setFeatures(allFeatures); + accessor.bclib_setFeatureSet(featureSet); + accessor.bclib_setFlowerFeatures(flowerFeatures); + } + + /** + * Adds new carver into existing biome. + * + * @param biome {@link Biome} to add carver in. + * @param carver {@link ConfiguredWorldCarver} to add. + * @param stage {@link Carving} stage. + */ + public static void addBiomeCarver(Biome biome, Holder> carver, Carving stage) { + BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) biome.getGenerationSettings(); + Map>> carverMap = CollectionsUtil.getMutable(accessor.bclib_getCarvers()); + HolderSet> carvers = carverMap.get(stage); + + List>> carverList; + if (carvers == null) { + carverList = Lists.newArrayList(); + } else { + carverList = carvers.stream().toList(); + } + carverList.add((Holder>) carver); + carverMap.put(stage, HolderSet.direct(carverList)); + accessor.bclib_setCarvers(carverMap); + } + + /** + * Adds surface rule to specified biome. + * + * @param biomeID biome {@link ResourceLocation}. + * @param source {@link SurfaceRules.RuleSource}. + */ + public static void addSurfaceRule(ResourceLocation biomeID, SurfaceRules.RuleSource source) { + SURFACE_RULES.put(biomeID, source); + //NOISE_GENERATOR_SETTINGS.forEach(BiomeAPI::changeSurfaceRulesForGenerator); + } + + /** + * Get surface rule for the biome using its {@link ResourceLocation} ID as a key. + * + * @param biomeID {@link ResourceLocation} biome ID. + * @return {@link SurfaceRules.RuleSource}. + */ + @Nullable + public static SurfaceRules.RuleSource getSurfaceRule(ResourceLocation biomeID) { + return SURFACE_RULES.get(biomeID); + } + + /** + * Adds mob spawning to specified biome. + * + * @param biome {@link Biome} to add mob spawning. + * @param entityType {@link BCLEntityWrapper} mob type. + * @param weight spawn weight. + * @param minGroupCount minimum mobs in group. + * @param maxGroupCount maximum mobs in group. + */ + public static void addBiomeMobSpawn(Holder biome, + BCLEntityWrapper entityType, + int weight, + int minGroupCount, + int maxGroupCount) { + if (entityType.canSpawn()) { + addBiomeMobSpawn(biome, entityType.type(), weight, minGroupCount, maxGroupCount); + } + } + + /** + * Adds mob spawning to specified biome. + * + * @param biome {@link Biome} to add mob spawning. + * @param entityType {@link EntityType} mob type. + * @param weight spawn weight. + * @param minGroupCount minimum mobs in group. + * @param maxGroupCount maximum mobs in group. + */ + public static void addBiomeMobSpawn(Holder biome, + EntityType entityType, + int weight, + int minGroupCount, + int maxGroupCount) { + final MobCategory category = entityType.getCategory(); + MobSpawnSettingsAccessor accessor = (MobSpawnSettingsAccessor) biome.value().getMobSettings(); + Map> spawners = CollectionsUtil.getMutable(accessor.bcl_getSpawners()); + List mobs = spawners.containsKey(category) + ? CollectionsUtil.getMutable(spawners.get(category) + .unwrap()) + : Lists.newArrayList(); + mobs.add(new SpawnerData(entityType, weight, minGroupCount, maxGroupCount)); + spawners.put(category, WeightedRandomList.create(mobs)); + accessor.bcl_setSpawners(spawners); + } + + /** + * Get biome surface block. Can be used to get terrain material for features or other things. + * + * @param pos {@link BlockPos} position to get block. + * @param biome {@link Holder} to get block from. + * @param level {@link ServerLevel} current server level. + * @return {@link BlockState} with the biome surface or AIR if it fails. + */ + public static BlockState getBiomeSurfaceBlock(BlockPos pos, Holder biome, ServerLevel level) { + ChunkGenerator generator = level.getChunkSource().getGenerator(); + if (generator instanceof NoiseBasedChunkGenerator) { + SurfaceProvider provider = (SurfaceProvider) generator; + return provider.bclib_getSurface(pos, biome, level); + } + return Blocks.AIR.defaultBlockState(); + } + + public static Optional findTopMaterial(WorldGenLevel world, BlockPos pos) { + return findTopMaterial(getBiome(world.getBiome(pos))); + } + + public static Optional findTopMaterial(Holder biome) { + return findTopMaterial(getBiome(biome.value())); + } + + public static Optional findTopMaterial(Biome biome) { + return findTopMaterial(getBiome(biome)); + } + + public static Optional findTopMaterial(BCLBiome biome) { + if (biome instanceof SurfaceMaterialProvider smp) { + return Optional.of(smp.getTopMaterial()); + } + return Optional.empty(); + } + + public static Optional findUnderMaterial(WorldGenLevel world, BlockPos pos) { + return findUnderMaterial(getBiome(world.getBiome(pos))); + } + + public static Optional findUnderMaterial(Holder biome) { + return findUnderMaterial(getBiome(biome.value())); + } + + public static Optional findUnderMaterial(Biome biome) { + return findUnderMaterial(getBiome(biome)); + } + + public static Optional findUnderMaterial(BCLBiome biome) { + if (biome instanceof SurfaceMaterialProvider smp) { + return Optional.of(smp.getUnderMaterial()); + } + return Optional.empty(); + } + + /** + * Set biome in chunk at specified position. + * + * @param chunk {@link ChunkAccess} chunk to set biome in. + * @param pos {@link BlockPos} biome position. + * @param biome {@link Holder} instance. Should be biome from world. + */ + public static void setBiome(ChunkAccess chunk, BlockPos pos, Holder biome) { + int sectionY = (pos.getY() - chunk.getMinBuildHeight()) >> 4; + PalettedContainer> biomes = chunk.getSection(sectionY).getBiomes(); + biomes.set((pos.getX() & 15) >> 2, (pos.getY() & 15) >> 2, (pos.getZ() & 15) >> 2, biome); + } + + /** + * Set biome in world at specified position. + * + * @param level {@link LevelAccessor} world to set biome in. + * @param pos {@link BlockPos} biome position. + * @param biome {@link Holder} instance. Should be biome from world. + */ + public static void setBiome(LevelAccessor level, BlockPos pos, Holder biome) { + ChunkAccess chunk = level.getChunk(pos); + setBiome(chunk, pos, biome); + } + + static class StructureID { + public final ResourceLocation biomeID; + public final Holder structure; + + StructureID(ResourceLocation biomeID, Holder structure) { + this.biomeID = biomeID; + this.structure = structure; + } + + @Override + public String toString() { + return "StructureID{" + "id=" + biomeID + ", structure=" + structure + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StructureID that = (StructureID) o; + return biomeID.equals(that.biomeID) && structure.equals(that.structure); + } + + @Override + public int hashCode() { + return Objects.hash(biomeID, structure); + } + } + + private static void sortFeatures(List> features) { + initFeatureOrder(); + + Set> featuresWithoutDuplicates = Sets.newHashSet(); + features.forEach(holder -> featuresWithoutDuplicates.add(holder)); + + if (featuresWithoutDuplicates.size() != features.size()) { + features.clear(); + featuresWithoutDuplicates.forEach(feature -> features.add(feature)); + } + + features.forEach(feature -> { + FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement()); + }); + + features.sort((f1, f2) -> { + int v1 = FEATURE_ORDER.getOrDefault(f1, 70000); + int v2 = FEATURE_ORDER.getOrDefault(f2, 70000); + return Integer.compare(v1, v2); + }); + } + + /** + * Getter for correct feature list from all biome feature list of lists. + * + * @param step feature {@link Decoration} step. + * @param lists biome accessor lists. + * @return mutable {@link ConfiguredFeature} list. + */ + private static List> getList(Decoration step, List>> lists) { + int index = step.ordinal(); + if (lists.size() <= index) { + for (int i = lists.size(); i <= index; i++) { + lists.add(Lists.newArrayList()); + } + } + List> list = CollectionsUtil.getMutable(lists.get(index)); + lists.set(index, list); + return list; + } + + private static List> getFeaturesListCopy(List> features, + Decoration step) { + return getFeaturesListCopy(features, step.ordinal()); + } + + private static List> getFeaturesListCopy(List> features, int index) { + while (features.size() <= index) { + features.add(HolderSet.direct(Lists.newArrayList())); + } + return features.get(index).stream().collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/BaseDataHandler.java b/src/main/java/org/betterx/bclib/api/dataexchange/BaseDataHandler.java new file mode 100644 index 00000000..e005cfde --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/BaseDataHandler.java @@ -0,0 +1,109 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +public abstract class BaseDataHandler { + private final boolean originatesOnServer; + @NotNull + private final ResourceLocation identifier; + + protected BaseDataHandler(ResourceLocation identifier, boolean originatesOnServer) { + this.originatesOnServer = originatesOnServer; + this.identifier = identifier; + } + + final public boolean getOriginatesOnServer() { + return originatesOnServer; + } + + final public ResourceLocation getIdentifier() { + return identifier; + } + + @Environment(EnvType.CLIENT) + abstract void receiveFromServer(Minecraft client, + ClientPacketListener handler, + FriendlyByteBuf buf, + PacketSender responseSender); + + private ServerPlayer lastMessageSender; + + void receiveFromClient(MinecraftServer server, + ServerPlayer player, + ServerGamePacketListenerImpl handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + lastMessageSender = player; + } + + final protected boolean reply(BaseDataHandler message, MinecraftServer server) { + if (lastMessageSender == null) return false; + message.sendToClient(server, lastMessageSender); + return true; + } + + abstract void sendToClient(MinecraftServer server); + + abstract void sendToClient(MinecraftServer server, ServerPlayer player); + + @Environment(EnvType.CLIENT) + abstract void sendToServer(Minecraft client); + + protected boolean isBlocking() { + return false; + } + + @Override + public String toString() { + return "BasDataHandler{" + "originatesOnServer=" + originatesOnServer + ", identifier=" + identifier + '}'; + } + + /** + * Write a String to a buffer (Convenience Method) + * + * @param buf The buffer to write to + * @param s The String you want to write + */ + public static void writeString(FriendlyByteBuf buf, String s) { + buf.writeByteArray(s.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Read a string from a buffer (Convenience Method) + * + * @param buf Thea buffer to read from + * @return The received String + */ + public static String readString(FriendlyByteBuf buf) { + byte[] data = buf.readByteArray(); + return new String(data, StandardCharsets.UTF_8); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BaseDataHandler)) return false; + BaseDataHandler that = (BaseDataHandler) o; + return originatesOnServer == that.originatesOnServer && identifier.equals(that.identifier); + } + + @Override + public int hashCode() { + return Objects.hash(originatesOnServer, identifier); + } +} + diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/Connector.java b/src/main/java/org/betterx/bclib/api/dataexchange/Connector.java new file mode 100644 index 00000000..8e43679c --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/Connector.java @@ -0,0 +1,19 @@ +package org.betterx.bclib.api.dataexchange; + +import org.betterx.bclib.api.dataexchange.handler.DataExchange; + +import java.util.Set; + +abstract class Connector { + protected final DataExchange api; + + Connector(DataExchange api) { + this.api = api; + } + + public abstract boolean onClient(); + + protected Set getDescriptors() { + return api.getDescriptors(); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/ConnectorClientside.java b/src/main/java/org/betterx/bclib/api/dataexchange/ConnectorClientside.java new file mode 100644 index 00000000..edfae0d0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/ConnectorClientside.java @@ -0,0 +1,77 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.FriendlyByteBuf; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketSender; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.handler.DataExchange; + +/** + * This is an internal class that handles a Clienetside players Connection to a Server + */ +@Environment(EnvType.CLIENT) +public class ConnectorClientside extends Connector { + private Minecraft client; + + ConnectorClientside(DataExchange api) { + super(api); + this.client = null; + } + + + @Override + public boolean onClient() { + return true; + } + + public void onPlayInit(ClientPacketListener handler, Minecraft client) { + if (this.client != null && this.client != client) { + BCLib.LOGGER.warning("Client changed!"); + } + this.client = client; + for (DataHandlerDescriptor desc : getDescriptors()) { + ClientPlayNetworking.registerReceiver(desc.IDENTIFIER, (_client, _handler, _buf, _responseSender) -> { + receiveFromServer(desc, _client, _handler, _buf, _responseSender); + }); + } + } + + public void onPlayReady(ClientPacketListener handler, PacketSender sender, Minecraft client) { + for (DataHandlerDescriptor desc : getDescriptors()) { + if (desc.sendOnJoin) { + BaseDataHandler h = desc.JOIN_INSTANCE.get(); + if (!h.getOriginatesOnServer()) { + h.sendToServer(client); + } + } + } + } + + public void onPlayDisconnect(ClientPacketListener handler, Minecraft client) { + for (DataHandlerDescriptor desc : getDescriptors()) { + ClientPlayNetworking.unregisterReceiver(desc.IDENTIFIER); + } + } + + void receiveFromServer(DataHandlerDescriptor desc, + Minecraft client, + ClientPacketListener handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + BaseDataHandler h = desc.INSTANCE.get(); + h.receiveFromServer(client, handler, buf, responseSender); + } + + public void sendToServer(BaseDataHandler h) { + if (client == null) { + throw new RuntimeException("[internal error] Client not initialized yet!"); + } + h.sendToServer(this.client); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/ConnectorServerside.java b/src/main/java/org/betterx/bclib/api/dataexchange/ConnectorServerside.java new file mode 100644 index 00000000..336ae883 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/ConnectorServerside.java @@ -0,0 +1,82 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; + +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.handler.DataExchange; + +/** + * This is an internal class that handles a Serverside Connection to a Client-Player + */ +public class ConnectorServerside extends Connector { + private MinecraftServer server; + + ConnectorServerside(DataExchange api) { + super(api); + server = null; + } + + @Override + public boolean onClient() { + return false; + } + + public void onPlayInit(ServerGamePacketListenerImpl handler, MinecraftServer server) { + if (this.server != null && this.server != server) { + BCLib.LOGGER.warning("Server changed!"); + } + this.server = server; + for (DataHandlerDescriptor desc : getDescriptors()) { + ServerPlayNetworking.registerReceiver(handler, + desc.IDENTIFIER, + (_server, _player, _handler, _buf, _responseSender) -> { + receiveFromClient(desc, + _server, + _player, + _handler, + _buf, + _responseSender); + }); + } + } + + public void onPlayReady(ServerGamePacketListenerImpl handler, PacketSender sender, MinecraftServer server) { + for (DataHandlerDescriptor desc : getDescriptors()) { + if (desc.sendOnJoin) { + BaseDataHandler h = desc.JOIN_INSTANCE.get(); + if (h.getOriginatesOnServer()) { + h.sendToClient(server, handler.player); + } + } + } + } + + public void onPlayDisconnect(ServerGamePacketListenerImpl handler, MinecraftServer server) { + for (DataHandlerDescriptor desc : getDescriptors()) { + ServerPlayNetworking.unregisterReceiver(handler, desc.IDENTIFIER); + } + } + + void receiveFromClient(DataHandlerDescriptor desc, + MinecraftServer server, + ServerPlayer player, + ServerGamePacketListenerImpl handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + BaseDataHandler h = desc.INSTANCE.get(); + h.receiveFromClient(server, player, handler, buf, responseSender); + } + + public void sendToClient(BaseDataHandler h) { + if (server == null) { + throw new RuntimeException("[internal error] Server not initialized yet!"); + } + h.sendToClient(this.server); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/DataExchangeAPI.java b/src/main/java/org/betterx/bclib/api/dataexchange/DataExchangeAPI.java new file mode 100644 index 00000000..ffdcab46 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/DataExchangeAPI.java @@ -0,0 +1,214 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.network.FriendlyByteBuf; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Lists; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.handler.DataExchange; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSync; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSyncID; +import org.betterx.bclib.config.Config; +import org.betterx.bclib.util.ModUtil; + +import java.io.File; +import java.util.List; +import java.util.function.BiConsumer; + +public class DataExchangeAPI extends DataExchange { + private final static List MODS = Lists.newArrayList(); + + /** + * You should never need to create a custom instance of this Object. + */ + public DataExchangeAPI() { + super(); + } + + @Environment(EnvType.CLIENT) + protected ConnectorClientside clientSupplier(DataExchange api) { + return new ConnectorClientside(api); + } + + protected ConnectorServerside serverSupplier(DataExchange api) { + return new ConnectorServerside(api); + } + + /** + * Register a mod to participate in the DataExchange. + * + * @param modID - {@link String} modID. + */ + public static void registerMod(String modID) { + if (!MODS.contains(modID)) MODS.add(modID); + } + + /** + * Register a mod dependency to participate in the DataExchange. + * + * @param modID - {@link String} modID. + */ + public static void registerModDependency(String modID) { + if (ModUtil.getModInfo(modID, false) != null && !"0.0.0".equals(ModUtil.getModVersion(modID))) { + registerMod(modID); + } else { + BCLib.LOGGER.info("Mod Dependency '" + modID + "' not found. This is probably OK."); + } + } + + /** + * Returns the IDs of all registered Mods. + * + * @return List of modIDs + */ + public static List registeredMods() { + return MODS; + } + + /** + * Add a new Descriptor for a {@link DataHandler}. + * + * @param desc The Descriptor you want to add. + */ + public static void registerDescriptor(DataHandlerDescriptor desc) { + DataExchange api = DataExchange.getInstance(); + api.getDescriptors() + .add(desc); + } + + /** + * Bulk-Add a Descriptors for your {@link DataHandler}-Objects. + * + * @param desc The Descriptors you want to add. + */ + public static void registerDescriptors(List desc) { + DataExchange api = DataExchange.getInstance(); + api.getDescriptors() + .addAll(desc); + } + + /** + * Sends the Handler. + *

+ * Depending on what the result of {@link DataHandler#getOriginatesOnServer()}, the Data is sent from the server + * to the client (if {@code true}) or the other way around. + *

+ * The method {@link DataHandler#serializeData(FriendlyByteBuf, boolean)} is called just before the data is sent. You should + * use this method to add the Data you need to the communication. + * + * @param h The Data that you want to send + */ + public static void send(BaseDataHandler h) { + if (h.getOriginatesOnServer()) { + DataExchangeAPI.getInstance().server.sendToClient(h); + } else { + DataExchangeAPI.getInstance().client.sendToServer(h); + } + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param fileName The name of the File + */ + public static void addAutoSyncFile(String modID, File fileName) { + AutoSync.addAutoSyncFileData(modID, fileName, false, SyncFileHash.NEED_TRANSFER); + } + + /** + * Registers a File for automatic client syncing. + *

+ * The file is synced of the {@link SyncFileHash} on client and server are not equal. This method will not copy the + * configs content from the client to the server. + * + * @param modID The ID of the calling Mod + * @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for + * Details + * @param fileName The name of the File + */ + public static void addAutoSyncFile(String modID, String uniqueID, File fileName) { + AutoSync.addAutoSyncFileData(modID, uniqueID, fileName, false, SyncFileHash.NEED_TRANSFER); + } + + /** + * Registers a File for automatic client syncing. + *

+ * The content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * if the File needs to be copied. Normally using the {@link SyncFileHash} + * for comparison is sufficient. + * + * @param modID The ID of the calling Mod + * @param fileName The name of the File + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + */ + public static void addAutoSyncFile(String modID, File fileName, AutoSync.NeedTransferPredicate needTransfer) { + AutoSync.addAutoSyncFileData(modID, fileName, true, needTransfer); + } + + /** + * Registers a File for automatic client syncing. + *

+ * The content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * if the File needs to be copied. Normally using the {@link SyncFileHash} + * for comparison is sufficient. + * + * @param modID The ID of the calling Mod + * @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for + * Details + * @param fileName The name of the File + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + */ + public static void addAutoSyncFile(String modID, + String uniqueID, + File fileName, + AutoSync.NeedTransferPredicate needTransfer) { + AutoSync.addAutoSyncFileData(modID, uniqueID, fileName, true, needTransfer); + } + + /** + * Register a function that is called whenever the client receives a file from the server and replaced toe local + * file with the new content. + *

+ * This callback is usefull if you need to reload the new content before the game is quit. + * + * @param callback A Function that receives the AutoSyncID as well as the Filename. + */ + public static void addOnWriteCallback(BiConsumer callback) { + AutoSync.addOnWriteCallback(callback); + } + + /** + * Returns the sync-folder for a given Mod. + *

+ * BCLib will ensure that the contents of sync-folder on the client is the same as the one on the server. + * + * @param modID ID of the Mod + * @return The path to the sync-folder + */ + public static File getModSyncFolder(String modID) { + File fl = AutoSync.SYNC_FOLDER.localFolder.resolve(modID.replace(".", "-") + .replace(":", "-") + .replace("\\", "-") + .replace("/", "-")) + .normalize() + .toFile(); + + if (!fl.exists()) { + fl.mkdirs(); + } + return fl; + } + + static { + addOnWriteCallback(Config::reloadSyncedConfig); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/DataHandler.java b/src/main/java/org/betterx/bclib/api/dataexchange/DataHandler.java new file mode 100644 index 00000000..620a458c --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/DataHandler.java @@ -0,0 +1,315 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.entity.player.Player; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.PlayerLookup; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.handler.autosync.Chunker; +import org.betterx.bclib.api.dataexchange.handler.autosync.Chunker.PacketChunkSender; + +import java.util.Collection; +import java.util.List; + +public abstract class DataHandler extends BaseDataHandler { + public abstract static class WithoutPayload extends DataHandler { + protected WithoutPayload(ResourceLocation identifier, boolean originatesOnServer) { + super(identifier, originatesOnServer); + } + + @Override + protected boolean prepareData(boolean isClient) { + return true; + } + + @Override + protected void serializeData(FriendlyByteBuf buf, boolean isClient) { + } + + @Override + protected void deserializeIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean isClient) { + } + } + + protected DataHandler(ResourceLocation identifier, boolean originatesOnServer) { + super(identifier, originatesOnServer); + } + + protected boolean prepareData(boolean isClient) { + return true; + } + + abstract protected void serializeData(FriendlyByteBuf buf, boolean isClient); + + abstract protected void deserializeIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean isClient); + + abstract protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient); + + + @Environment(EnvType.CLIENT) + @Override + void receiveFromServer(Minecraft client, + ClientPacketListener handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + deserializeIncomingData(buf, responseSender, true); + final Runnable runner = () -> runOnGameThread(client, null, true); + + if (isBlocking()) client.executeBlocking(runner); + else client.execute(runner); + } + + @Override + void receiveFromClient(MinecraftServer server, + ServerPlayer player, + ServerGamePacketListenerImpl handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + super.receiveFromClient(server, player, handler, buf, responseSender); + + deserializeIncomingData(buf, responseSender, false); + final Runnable runner = () -> runOnGameThread(null, server, false); + + if (isBlocking()) server.executeBlocking(runner); + else server.execute(runner); + } + + @Override + void sendToClient(MinecraftServer server) { + if (prepareData(false)) { + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeData(buf, false); + + _sendToClient(getIdentifier(), server, PlayerLookup.all(server), buf); + } + } + + @Override + void sendToClient(MinecraftServer server, ServerPlayer player) { + if (prepareData(false)) { + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeData(buf, false); + + _sendToClient(getIdentifier(), server, List.of(player), buf); + } + } + + + public static void _sendToClient(ResourceLocation identifier, + MinecraftServer server, + Collection players, + FriendlyByteBuf buf) { + if (buf.readableBytes() > Chunker.MAX_PACKET_SIZE) { + final PacketChunkSender sender = new PacketChunkSender(buf, identifier); + sender.sendChunks(players); + } else { + for (ServerPlayer player : players) { + ServerPlayNetworking.send(player, identifier, buf); + } + } + } + + @Environment(EnvType.CLIENT) + @Override + void sendToServer(Minecraft client) { + if (prepareData(true)) { + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeData(buf, true); + ClientPlayNetworking.send(getIdentifier(), buf); + } + } + + /** + * A Message that always originates on the Client + */ + public abstract static class FromClient extends BaseDataHandler { + public abstract static class WithoutPayload extends FromClient { + protected WithoutPayload(ResourceLocation identifier) { + super(identifier); + } + + @Override + protected boolean prepareDataOnClient() { + return true; + } + + @Override + protected void serializeDataOnClient(FriendlyByteBuf buf) { + } + + @Override + protected void deserializeIncomingDataOnServer(FriendlyByteBuf buf, + Player player, + PacketSender responseSender) { + } + } + + protected FromClient(ResourceLocation identifier) { + super(identifier, false); + } + + @Environment(EnvType.CLIENT) + protected boolean prepareDataOnClient() { + return true; + } + + @Environment(EnvType.CLIENT) + abstract protected void serializeDataOnClient(FriendlyByteBuf buf); + + protected abstract void deserializeIncomingDataOnServer(FriendlyByteBuf buf, + Player player, + PacketSender responseSender); + protected abstract void runOnServerGameThread(MinecraftServer server, Player player); + + @Environment(EnvType.CLIENT) + @Override + void receiveFromServer(Minecraft client, + ClientPacketListener handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the client!"); + } + + @Override + void receiveFromClient(MinecraftServer server, + ServerPlayer player, + ServerGamePacketListenerImpl handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + super.receiveFromClient(server, player, handler, buf, responseSender); + + deserializeIncomingDataOnServer(buf, player, responseSender); + final Runnable runner = () -> runOnServerGameThread(server, player); + + if (isBlocking()) server.executeBlocking(runner); + else server.execute(runner); + } + + @Override + void sendToClient(MinecraftServer server) { + BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the client!"); + } + + @Override + void sendToClient(MinecraftServer server, ServerPlayer player) { + BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the client!"); + } + + @Environment(EnvType.CLIENT) + @Override + void sendToServer(Minecraft client) { + if (prepareDataOnClient()) { + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeDataOnClient(buf); + ClientPlayNetworking.send(getIdentifier(), buf); + } + } + } + + /** + * A Message that always originates on the Server + */ + public abstract static class FromServer extends BaseDataHandler { + public abstract static class WithoutPayload extends FromServer { + protected WithoutPayload(ResourceLocation identifier) { + super(identifier); + } + + @Override + protected boolean prepareDataOnServer() { + return true; + } + + @Override + protected void serializeDataOnServer(FriendlyByteBuf buf) { + } + + @Override + protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { + } + } + + protected FromServer(ResourceLocation identifier) { + super(identifier, true); + } + + protected boolean prepareDataOnServer() { + return true; + } + + abstract protected void serializeDataOnServer(FriendlyByteBuf buf); + + @Environment(EnvType.CLIENT) + abstract protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender); + + @Environment(EnvType.CLIENT) + abstract protected void runOnClientGameThread(Minecraft client); + + + @Environment(EnvType.CLIENT) + @Override + final void receiveFromServer(Minecraft client, + ClientPacketListener handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + deserializeIncomingDataOnClient(buf, responseSender); + final Runnable runner = () -> runOnClientGameThread(client); + + if (isBlocking()) client.executeBlocking(runner); + else client.execute(runner); + } + + @Override + final void receiveFromClient(MinecraftServer server, + ServerPlayer player, + ServerGamePacketListenerImpl handler, + FriendlyByteBuf buf, + PacketSender responseSender) { + super.receiveFromClient(server, player, handler, buf, responseSender); + BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the server!"); + } + + public void receiveFromMemory(FriendlyByteBuf buf) { + receiveFromServer(Minecraft.getInstance(), null, buf, null); + } + + @Override + final void sendToClient(MinecraftServer server) { + if (prepareDataOnServer()) { + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeDataOnServer(buf); + + _sendToClient(getIdentifier(), server, PlayerLookup.all(server), buf); + } + } + + @Override + final void sendToClient(MinecraftServer server, ServerPlayer player) { + if (prepareDataOnServer()) { + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeDataOnServer(buf); + + _sendToClient(getIdentifier(), server, List.of(player), buf); + } + } + + @Environment(EnvType.CLIENT) + @Override + final void sendToServer(Minecraft client) { + BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the server!"); + } + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/DataHandlerDescriptor.java b/src/main/java/org/betterx/bclib/api/dataexchange/DataHandlerDescriptor.java new file mode 100644 index 00000000..14de38d1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/DataHandlerDescriptor.java @@ -0,0 +1,57 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.resources.ResourceLocation; + +import java.util.Objects; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +public class DataHandlerDescriptor { + public DataHandlerDescriptor(@NotNull ResourceLocation identifier, @NotNull Supplier instancer) { + this(identifier, instancer, instancer, false, false); + } + + public DataHandlerDescriptor(@NotNull ResourceLocation identifier, + @NotNull Supplier instancer, + boolean sendOnJoin, + boolean sendBeforeEnter) { + this(identifier, instancer, instancer, sendOnJoin, sendBeforeEnter); + } + + public DataHandlerDescriptor(@NotNull ResourceLocation identifier, + @NotNull Supplier receiv_instancer, + @NotNull Supplier join_instancer, + boolean sendOnJoin, + boolean sendBeforeEnter) { + this.INSTANCE = receiv_instancer; + this.JOIN_INSTANCE = join_instancer; + this.IDENTIFIER = identifier; + + this.sendOnJoin = sendOnJoin; + this.sendBeforeEnter = sendBeforeEnter; + } + + public final boolean sendOnJoin; + public final boolean sendBeforeEnter; + @NotNull + public final ResourceLocation IDENTIFIER; + @NotNull + public final Supplier INSTANCE; + @NotNull + public final Supplier JOIN_INSTANCE; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof ResourceLocation) { + return o.equals(IDENTIFIER); + } + if (!(o instanceof DataHandlerDescriptor that)) return false; + return IDENTIFIER.equals(that.IDENTIFIER); + } + + @Override + public int hashCode() { + return Objects.hash(IDENTIFIER); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/FileHash.java b/src/main/java/org/betterx/bclib/api/dataexchange/FileHash.java new file mode 100644 index 00000000..ee0ff075 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/FileHash.java @@ -0,0 +1,160 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.network.FriendlyByteBuf; + +import org.betterx.bclib.BCLib; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +public class FileHash { + private static final int ERR_DOES_NOT_EXIST = -10; + private static final int ERR_IO_ERROR = -20; + + /** + * The md5-hash of the file + */ + @NotNull + public final byte[] md5; + + /** + * The size (in bytes) of the input. + */ + public final int size; + + /** + * a value that is directly calculated from defined byte positions. + */ + public final int value; + + FileHash(byte[] md5, int size, int value) { + Objects.nonNull(md5); + + this.md5 = md5; + this.size = size; + this.value = value; + } + + static FileHash createForEmpty(int errCode) { + return new FileHash(new byte[0], 0, errCode); + } + + public boolean noFile() { + return md5.length == 0; + } + + /** + * Serializes the Object to a buffer + * + * @param buf The buffer to write to + */ + public void serialize(FriendlyByteBuf buf) { + buf.writeInt(size); + buf.writeInt(value); + buf.writeByteArray(md5); + } + + /** + * Deserialize a Buffer to a new {@link SyncFileHash}-Object + * + * @param buf Thea buffer to read from + * @return The received String + */ + public static FileHash deserialize(FriendlyByteBuf buf) { + final int size = buf.readInt(); + final int value = buf.readInt(); + final byte[] md5 = buf.readByteArray(); + + return new FileHash(md5, size, value); + } + + /** + * Convert the md5-hash to a human readable string + * + * @return The converted String + */ + public String getMd5String() { + return toHexString(md5); + } + + /** + * Converts a byte-array to a hex-string representation + * + * @param bytes The source array + * @return The resulting string, or an empty String if the input was {@code null} + */ + public static String toHexString(byte[] bytes) { + if (bytes == null) return ""; + + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + /** + * Create a new {@link FileHash}. + * + * @param file The input file + * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are + * identical. Will return {@code null} when an error occurs or the File does not exist + */ + public static FileHash create(File file) { + if (!file.exists()) return createForEmpty(ERR_DOES_NOT_EXIST); + final Path path = file.toPath(); + + int size = 0; + byte[] md5 = new byte[0]; + int value = 0; + + try { + byte[] data = Files.readAllBytes(path); + + size = data.length; + + value = size > 0 ? (data[size / 3] | (data[size / 2] << 8) | (data[size / 5] << 16)) : -1; + if (size > 20) value |= data[20] << 24; + + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(data); + md5 = md.digest(); + + return new FileHash(md5, size, value); + } catch (IOException e) { + BCLib.LOGGER.error("Failed to read file: " + file); + return null; + } catch (NoSuchAlgorithmException e) { + BCLib.LOGGER.error("Unable to build hash for file: " + file); + } + + return createForEmpty(ERR_IO_ERROR); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof FileHash)) return false; + FileHash fileHash = (FileHash) o; + return size == fileHash.size && value == fileHash.value && Arrays.equals(md5, fileHash.md5); + } + + @Override + public int hashCode() { + int result = Objects.hash(size, value); + result = 31 * result + Arrays.hashCode(md5); + return result; + } + + @Override + public String toString() { + return String.format("%08x", size) + "-" + String.format("%08x", value) + "-" + getMd5String(); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/SyncFileHash.java b/src/main/java/org/betterx/bclib/api/dataexchange/SyncFileHash.java new file mode 100644 index 00000000..cf1e40d0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/SyncFileHash.java @@ -0,0 +1,113 @@ +package org.betterx.bclib.api.dataexchange; + +import net.minecraft.network.FriendlyByteBuf; + +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSync; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSyncID; + +import java.io.File; +import java.util.Objects; + +/** + * Calculates a hash based on the contents of a File. + *

+ * A File-Hash contains the md5-sum of the File, as well as its size and byte-values from defined positions + *

+ * You can compare instances using {@link #equals(Object)} to determine if two files are + * identical. + */ +public class SyncFileHash extends AutoSyncID { + public final FileHash hash; + + SyncFileHash(String modID, File file, byte[] md5, int size, int value) { + this(modID, file.getName(), md5, size, value); + } + + SyncFileHash(String modID, String uniqueID, byte[] md5, int size, int value) { + this(modID, uniqueID, new FileHash(md5, size, value)); + } + + SyncFileHash(String modID, File file, FileHash hash) { + this(modID, file.getName(), hash); + } + + SyncFileHash(String modID, String uniqueID, FileHash hash) { + super(modID, uniqueID); + this.hash = hash; + } + + + final static AutoSync.NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content) -> !clientHash.equals( + serverHash); + + @Override + public String toString() { + return super.toString() + ": " + hash.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SyncFileHash)) return false; + if (!super.equals(o)) return false; + SyncFileHash that = (SyncFileHash) o; + return hash.equals(that.hash); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), hash); + } + + /** + * Serializes the Object to a buffer + * + * @param buf The buffer to write to + */ + public void serialize(FriendlyByteBuf buf) { + hash.serialize(buf); + DataHandler.writeString(buf, modID); + DataHandler.writeString(buf, uniqueID); + } + + /** + * Deserialize a Buffer to a new {@link SyncFileHash}-Object + * + * @param buf Thea buffer to read from + * @return The received String + */ + public static SyncFileHash deserialize(FriendlyByteBuf buf) { + final FileHash hash = FileHash.deserialize(buf); + final String modID = DataHandler.readString(buf); + final String uniqueID = DataHandler.readString(buf); + + return new SyncFileHash(modID, uniqueID, hash); + } + + /** + * Create a new {@link SyncFileHash}. + *

+ * Will call {@link #create(String, File, String)} using the name of the File as {@code uniqueID}. + * + * @param modID ID of the calling Mod + * @param file The input file + * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are + * identical. Will return {@code null} when an error occurs or the File does not exist + */ + public static SyncFileHash create(String modID, File file) { + return create(modID, file, file.getName()); + } + + /** + * Create a new {@link SyncFileHash}. + * + * @param modID ID of the calling Mod + * @param file The input file + * @param uniqueID The unique ID that is used for this File (see {@link SyncFileHash#uniqueID} for Details. + * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are + * identical. Will return {@code null} when an error occurs or the File does not exist + */ + public static SyncFileHash create(String modID, File file, String uniqueID) { + return new SyncFileHash(modID, uniqueID, FileHash.create(file)); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/DataExchange.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/DataExchange.java new file mode 100644 index 00000000..773a5cbd --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/DataExchange.java @@ -0,0 +1,111 @@ +package org.betterx.bclib.api.dataexchange.handler; + +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; + +import org.betterx.bclib.api.dataexchange.*; + +import java.util.HashSet; +import java.util.Set; + +abstract public class DataExchange { + + + private static DataExchangeAPI instance; + + protected static DataExchangeAPI getInstance() { + if (instance == null) { + instance = new DataExchangeAPI(); + } + return instance; + } + + protected ConnectorServerside server; + protected ConnectorClientside client; + protected final Set descriptors; + + + private final boolean didLoadSyncFolder = false; + + abstract protected ConnectorClientside clientSupplier(DataExchange api); + + abstract protected ConnectorServerside serverSupplier(DataExchange api); + + protected DataExchange() { + descriptors = new HashSet<>(); + } + + public Set getDescriptors() { + return descriptors; + } + + public static DataHandlerDescriptor getDescriptor(ResourceLocation identifier) { + return getInstance().descriptors.stream().filter(d -> d.equals(identifier)).findFirst().orElse(null); + } + + @Environment(EnvType.CLIENT) + protected void initClientside() { + if (client != null) return; + client = clientSupplier(this); + + ClientPlayConnectionEvents.INIT.register(client::onPlayInit); + ClientPlayConnectionEvents.JOIN.register(client::onPlayReady); + ClientPlayConnectionEvents.DISCONNECT.register(client::onPlayDisconnect); + } + + protected void initServerSide() { + if (server != null) return; + server = serverSupplier(this); + + ServerPlayConnectionEvents.INIT.register(server::onPlayInit); + ServerPlayConnectionEvents.JOIN.register(server::onPlayReady); + ServerPlayConnectionEvents.DISCONNECT.register(server::onPlayDisconnect); + } + + /** + * Initializes all datastructures that need to exist in the client component. + *

+ * This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called + */ + @Environment(EnvType.CLIENT) + public static void prepareClientside() { + DataExchange api = DataExchange.getInstance(); + api.initClientside(); + + } + + /** + * Initializes all datastructures that need to exist in the server component. + *

+ * This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called + */ + public static void prepareServerside() { + DataExchange api = DataExchange.getInstance(); + api.initServerSide(); + } + + + /** + * Automatically called before the player enters the world. + *

+ * This is automatically called by BCLib. It will send all {@link DataHandler}-Objects that have {@link DataHandlerDescriptor#sendBeforeEnter} set to* + * {@code true}, + */ + @Environment(EnvType.CLIENT) + public static void sendOnEnter() { + getInstance().descriptors.forEach((desc) -> { + if (desc.sendBeforeEnter) { + BaseDataHandler h = desc.JOIN_INSTANCE.get(); + if (!h.getOriginatesOnServer()) { + getInstance().client.sendToServer(h); + } + } + }); + } + + +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java new file mode 100644 index 00000000..f3d228e8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java @@ -0,0 +1,249 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.network.FriendlyByteBuf; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.SyncFileHash; +import org.betterx.bclib.util.ModUtil; +import org.betterx.bclib.util.ModUtil.ModInfo; +import org.betterx.bclib.util.Pair; +import org.betterx.bclib.util.PathUtil; +import org.betterx.bclib.util.Triple; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +class AutoFileSyncEntry extends AutoSyncID { + static class ForDirectFileRequest extends AutoFileSyncEntry { + final File relFile; + + ForDirectFileRequest(String syncID, File relFile, File absFile) { + super(AutoSyncID.ForDirectFileRequest.MOD_ID, syncID, absFile, false, (a, b, c) -> false); + this.relFile = relFile; + } + + @Override + public int serializeContent(FriendlyByteBuf buf) { + int res = super.serializeContent(buf); + DataHandler.writeString(buf, relFile.toString()); + + return res; + } + + static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf) { + final String relFile = DataHandler.readString(buf); + SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(syncID); + if (desc != null) { + //ensures that the file is not above the base-folder + if (desc.acceptChildElements(desc.mapAbsolute(relFile))) { + return new AutoFileSyncEntry.ForDirectFileRequest(syncID, + new File(relFile), + desc.localFolder.resolve(relFile) + .normalize() + .toFile()); + } + } + return null; + } + + @Override + public String toString() { + return uniqueID + " - " + relFile; + } + } + + static class ForModFileRequest extends AutoFileSyncEntry { + public static File getLocalPathForID(String modID, boolean matchLocalVersion) { + ModInfo mi = ModUtil.getModInfo(modID, matchLocalVersion); + if (mi != null) { + return mi.jarPath.toFile(); + } + return null; + } + + public final String version; + + ForModFileRequest(String modID, boolean matchLocalVersion, String version) { + super(modID, + AutoSyncID.ForModFileRequest.UNIQUE_ID, + getLocalPathForID(modID, matchLocalVersion), + false, + (a, b, c) -> false); + if (this.fileName == null && matchLocalVersion) { + BCLib.LOGGER.error("Unknown mod '" + modID + "'."); + } + if (version == null) + this.version = ModUtil.getModVersion(modID); + else + this.version = version; + } + + @Override + public int serializeContent(FriendlyByteBuf buf) { + final int res = super.serializeContent(buf); + buf.writeInt(ModUtil.convertModVersion(version)); + return res; + } + + static AutoFileSyncEntry.ForModFileRequest finishDeserializeContent(String modID, FriendlyByteBuf buf) { + final String version = ModUtil.convertModVersion(buf.readInt()); + return new AutoFileSyncEntry.ForModFileRequest(modID, false, version); + } + + @Override + public String toString() { + return "Mod " + modID + " (v" + version + ")"; + } + } + + public final AutoSync.NeedTransferPredicate needTransfer; + public final File fileName; + public final boolean requestContent; + private SyncFileHash hash; + + AutoFileSyncEntry(String modID, + File fileName, + boolean requestContent, + AutoSync.NeedTransferPredicate needTransfer) { + this(modID, fileName.getName(), fileName, requestContent, needTransfer); + } + + AutoFileSyncEntry(String modID, + String uniqueID, + File fileName, + boolean requestContent, + AutoSync.NeedTransferPredicate needTransfer) { + super(modID, uniqueID); + this.needTransfer = needTransfer; + this.fileName = fileName; + this.requestContent = requestContent; + } + + + public SyncFileHash getFileHash() { + if (hash == null) { + hash = SyncFileHash.create(modID, fileName, uniqueID); + } + return hash; + } + + public byte[] getContent() { + if (!fileName.exists()) return new byte[0]; + final Path path = fileName.toPath(); + + try { + return Files.readAllBytes(path); + } catch (IOException e) { + + } + return new byte[0]; + } + + public int serializeContent(FriendlyByteBuf buf) { + DataHandler.writeString(buf, modID); + DataHandler.writeString(buf, uniqueID); + return serializeFileContent(buf); + } + + public static Triple deserializeContent(FriendlyByteBuf buf) { + final String modID = DataHandler.readString(buf); + final String uniqueID = DataHandler.readString(buf); + byte[] data = deserializeFileContent(buf); + + AutoFileSyncEntry entry; + if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)) { + entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf); + } else if (AutoSyncID.ForModFileRequest.UNIQUE_ID.equals(uniqueID)) { + entry = AutoFileSyncEntry.ForModFileRequest.finishDeserializeContent(modID, buf); + } else { + entry = AutoFileSyncEntry.findMatching(modID, uniqueID); + } + return new Triple<>(entry, data, new AutoSyncID(modID, uniqueID)); + } + + + public void serialize(FriendlyByteBuf buf) { + getFileHash().serialize(buf); + buf.writeBoolean(requestContent); + + if (requestContent) { + serializeFileContent(buf); + } + } + + public static AutoSync.AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf) { + Pair e = deserialize(buf); + AutoFileSyncEntry match = findMatching(e.first); + return new AutoSync.AutoSyncTriple(e.first, e.second, match); + } + + public static Pair deserialize(FriendlyByteBuf buf) { + SyncFileHash hash = SyncFileHash.deserialize(buf); + boolean withContent = buf.readBoolean(); + byte[] data = null; + if (withContent) { + data = deserializeFileContent(buf); + } + + return new Pair(hash, data); + } + + private int serializeFileContent(FriendlyByteBuf buf) { + if (!PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())) { + BCLib.LOGGER.error(fileName + " is not within game folder " + PathUtil.GAME_FOLDER + ". Pretending it does not exist."); + buf.writeInt(0); + return 0; + } + + byte[] content = getContent(); + buf.writeInt(content.length); + buf.writeByteArray(content); + return content.length; + } + + private static byte[] deserializeFileContent(FriendlyByteBuf buf) { + byte[] data; + int size = buf.readInt(); + data = buf.readByteArray(size); + return data; + } + + + public static AutoFileSyncEntry findMatching(SyncFileHash hash) { + return findMatching(hash.modID, hash.uniqueID); + } + + public static AutoFileSyncEntry findMatching(AutoSyncID aid) { + if (aid instanceof AutoSyncID.ForDirectFileRequest) { + AutoSyncID.ForDirectFileRequest freq = (AutoSyncID.ForDirectFileRequest) aid; + SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(freq.uniqueID); + if (desc != null) { + SyncFolderDescriptor.SubFile subFile = desc.getLocalSubFile(freq.relFile.toString()); + if (subFile != null) { + final File absPath = desc.localFolder.resolve(subFile.relPath) + .normalize() + .toFile(); + return new AutoFileSyncEntry.ForDirectFileRequest(freq.uniqueID, + new File(subFile.relPath), + absPath); + } + } + return null; + } else if (aid instanceof AutoSyncID.ForModFileRequest) { + AutoSyncID.ForModFileRequest mreq = (AutoSyncID.ForModFileRequest) aid; + return new AutoFileSyncEntry.ForModFileRequest(mreq.modID, true, null); + } + return findMatching(aid.modID, aid.uniqueID); + } + + public static AutoFileSyncEntry findMatching(String modID, String uniqueID) { + return AutoSync.getAutoSyncFiles() + .stream() + .filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID)) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoSync.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoSync.java new file mode 100644 index 00000000..5802c2a1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoSync.java @@ -0,0 +1,199 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.fabricmc.loader.api.FabricLoader; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.api.dataexchange.SyncFileHash; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.config.ServerConfig; +import org.betterx.bclib.util.PathUtil; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.BiConsumer; + +public class AutoSync { + public static final String SYNC_CATEGORY = "auto_sync"; + public final static SyncFolderDescriptor SYNC_FOLDER = new SyncFolderDescriptor("BCLIB-SYNC", + FabricLoader.getInstance() + .getGameDir() + .resolve("bclib-sync") + .normalize() + .toAbsolutePath(), + true); + + @FunctionalInterface + public interface NeedTransferPredicate { + boolean test(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content); + } + + final static class AutoSyncTriple { + public final SyncFileHash serverHash; + public final byte[] serverContent; + public final AutoFileSyncEntry localMatch; + + public AutoSyncTriple(SyncFileHash serverHash, byte[] serverContent, AutoFileSyncEntry localMatch) { + this.serverHash = serverHash; + this.serverContent = serverContent; + this.localMatch = localMatch; + } + + @Override + public String toString() { + return serverHash.modID + "." + serverHash.uniqueID; + } + } + + + // ##### File Syncing + protected final static List> onWriteCallbacks = new ArrayList<>(2); + + /** + * Register a function that is called whenever the client receives a file from the server and replaced toe local + * file with the new content. + *

+ * This callback is usefull if you need to reload the new content before the game is quit. + * + * @param callback A Function that receives the AutoSyncID as well as the Filename. + */ + public static void addOnWriteCallback(BiConsumer callback) { + onWriteCallbacks.add(callback); + } + + private static final List autoSyncFiles = new ArrayList<>(4); + + public static List getAutoSyncFiles() { + return autoSyncFiles; + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + * @param fileName The name of the File + * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * If the File needs to be copied. Normally using the {@link SyncFileHash} + * for comparison is sufficient. + */ + public static void addAutoSyncFileData(String modID, + File fileName, + boolean requestContent, + NeedTransferPredicate needTransfer) { + if (!PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())) { + BCLib.LOGGER.error(fileName + " is outside of Game Folder " + PathUtil.GAME_FOLDER); + } else { + autoSyncFiles.add(new AutoFileSyncEntry(modID, fileName, requestContent, needTransfer)); + } + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for + * Details + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + * @param fileName The name of the File + * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * If the File needs to be copied. Normally using the {@link SyncFileHash} + * for comparison is sufficient. + */ + public static void addAutoSyncFileData(String modID, + String uniqueID, + File fileName, + boolean requestContent, + NeedTransferPredicate needTransfer) { + if (!PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())) { + BCLib.LOGGER.error(fileName + " is outside of Game Folder " + PathUtil.GAME_FOLDER); + } else { + autoSyncFiles.add(new AutoFileSyncEntry(modID, uniqueID, fileName, requestContent, needTransfer)); + } + } + + /** + * Called when {@code SendFiles} received a File on the Client and wrote it to the FileSystem. + *

+ * This is the place where reload Code should go. + * + * @param aid The ID of the received File + * @param file The location of the FIle on the client + */ + static void didReceiveFile(AutoSyncID aid, File file) { + onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file)); + } + + + // ##### Folder Syncing + static final List syncFolderDescriptions = Arrays.asList(SYNC_FOLDER); + + private List syncFolderContent; + + protected List getSyncFolderContent() { + if (syncFolderContent == null) { + return new ArrayList<>(0); + } + return syncFolderContent; + } + + private static boolean didRegisterAdditionalMods = false; + + //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()); + } + + if (!didRegisterAdditionalMods && Configs.SERVER_CONFIG.isOfferingMods()) { + didRegisterAdditionalMods = true; + List modIDs = Configs.SERVER_CONFIG.get(ServerConfig.ADDITIONAL_MODS); + if (modIDs != null) { + modIDs.stream().forEach(modID -> DataExchangeAPI.registerModDependency(modID)); + } + } + + } + + protected static SyncFolderDescriptor getSyncFolderDescriptor(String folderID) { + return syncFolderDescriptions.stream() + .filter(d -> d.equals(folderID)) + .findFirst() + .orElse(null); + } + + protected static Path localBasePathForFolderID(String folderID) { + final SyncFolderDescriptor desc = getSyncFolderDescriptor(folderID); + if (desc != null) { + return desc.localFolder; + } else { + BCLib.LOGGER.warning("Unknown Sync-Folder ID '" + folderID + "'"); + return null; + } + } + + public static void registerSyncFolder(String folderID, Path localBaseFolder, boolean removeAdditionalFiles) { + localBaseFolder = localBaseFolder.normalize(); + if (PathUtil.isChildOf(PathUtil.GAME_FOLDER, localBaseFolder)) { + final SyncFolderDescriptor desc = new SyncFolderDescriptor(folderID, + localBaseFolder, + removeAdditionalFiles); + if (syncFolderDescriptions.contains(desc)) { + BCLib.LOGGER.warning("Tried to override Folder Sync '" + folderID + "' again."); + } else { + syncFolderDescriptions.add(desc); + } + } else { + BCLib.LOGGER.error(localBaseFolder + " (from " + folderID + ") is outside the game directory " + PathUtil.GAME_FOLDER + ". Sync is not allowed."); + } + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoSyncID.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoSyncID.java new file mode 100644 index 00000000..c7085bb5 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/AutoSyncID.java @@ -0,0 +1,144 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.network.FriendlyByteBuf; + +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.config.Config; +import org.betterx.bclib.util.ModUtil; + +import java.io.File; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +public class AutoSyncID { + static class WithContentOverride extends AutoSyncID { + final FileContentWrapper contentWrapper; + final File localFile; + + WithContentOverride(String modID, String uniqueID, FileContentWrapper contentWrapper, File localFile) { + super(modID, uniqueID); + this.contentWrapper = contentWrapper; + this.localFile = localFile; + } + + @Override + public String toString() { + return super.toString() + " (Content override)"; + } + } + + static class ForDirectFileRequest extends AutoSyncID { + public final static String MOD_ID = "bclib::FILE"; + final File relFile; + + ForDirectFileRequest(String syncID, File relFile) { + super(ForDirectFileRequest.MOD_ID, syncID); + this.relFile = relFile; + } + + @Override + void serializeData(FriendlyByteBuf buf) { + super.serializeData(buf); + DataHandler.writeString(buf, relFile.toString()); + } + + static ForDirectFileRequest finishDeserialize(String modID, String uniqueID, FriendlyByteBuf buf) { + final File fl = new File(DataHandler.readString(buf)); + return new ForDirectFileRequest(uniqueID, fl); + } + + @Override + public String toString() { + return super.uniqueID + " (" + this.relFile + ")"; + } + } + + static class ForModFileRequest extends AutoSyncID { + public final static String UNIQUE_ID = "bclib::MOD"; + private final String version; + + ForModFileRequest(String modID, String version) { + super(modID, ForModFileRequest.UNIQUE_ID); + this.version = version; + } + + @Override + void serializeData(FriendlyByteBuf buf) { + super.serializeData(buf); + buf.writeInt(ModUtil.convertModVersion(version)); + } + + static ForModFileRequest finishDeserialize(String modID, String uniqueID, FriendlyByteBuf buf) { + final String version = ModUtil.convertModVersion(buf.readInt()); + return new ForModFileRequest(modID, version); + } + + @Override + public String toString() { + return super.modID + " (v" + this.version + ")"; + } + } + + /** + * A Unique ID for the referenced File. + *

+ * Files with the same {@link #modID} need to have a unique IDs. Normally the filename from FileHash(String, File, byte[], int, int) + * is used to generated that ID, but you can directly specify one using FileHash(String, String, byte[], int, int). + */ + @NotNull + public final String uniqueID; + + /** + * The ID of the Mod that is registering the File + */ + @NotNull + public final String modID; + + public AutoSyncID(String modID, String uniqueID) { + Objects.nonNull(modID); + Objects.nonNull(uniqueID); + + this.modID = modID; + this.uniqueID = uniqueID; + } + + @Override + public String toString() { + return modID + "." + uniqueID; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AutoSyncID)) return false; + AutoSyncID that = (AutoSyncID) o; + return uniqueID.equals(that.uniqueID) && modID.equals(that.modID); + } + + @Override + public int hashCode() { + return Objects.hash(uniqueID, modID); + } + + void serializeData(FriendlyByteBuf buf) { + DataHandler.writeString(buf, modID); + DataHandler.writeString(buf, uniqueID); + } + + static AutoSyncID deserializeData(FriendlyByteBuf buf) { + String modID = DataHandler.readString(buf); + String uID = DataHandler.readString(buf); + + if (ForDirectFileRequest.MOD_ID.equals(modID)) { + return ForDirectFileRequest.finishDeserialize(modID, uID, buf); + } else if (ForModFileRequest.UNIQUE_ID.equals(uID)) { + return ForModFileRequest.finishDeserialize(modID, uID, buf); + } else { + return new AutoSyncID(modID, uID); + } + } + + public boolean isConfigFile() { + return this.uniqueID.startsWith(Config.CONFIG_SYNC_PREFIX); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/Chunker.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/Chunker.java new file mode 100644 index 00000000..e54186bd --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/Chunker.java @@ -0,0 +1,276 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProgressListener; + +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 org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.BaseDataHandler; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.DataHandlerDescriptor; +import org.betterx.bclib.api.dataexchange.handler.DataExchange; + +import java.util.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Used to seperate large data transfers into multiple smaller messages. + *

+ * {@link DataHandler} will automatically convert larger messages into Chunks on the Server + * and assemble the original message from those chunks on the client. + */ +public class Chunker extends DataHandler.FromServer { + + /** + * Responsible for assembling the original ByteBuffer created by {@link PacketChunkSender} on the + * receiving end. Automatically created from the header {@link Chunker}-Message (where the serialNo==-1) + */ + static class PacketChunkReceiver { + @NotNull + public final UUID uuid; + public final int chunkCount; + @NotNull + private final FriendlyByteBuf networkedBuf; + @Nullable + private final DataHandlerDescriptor descriptor; + + private static final List active = new ArrayList<>(1); + + private static PacketChunkReceiver newReceiver(@NotNull UUID uuid, int chunkCount, ResourceLocation origin) { + DataHandlerDescriptor desc = DataExchange.getDescriptor(origin); + final PacketChunkReceiver r = new PacketChunkReceiver(uuid, chunkCount, desc); + active.add(r); + return r; + } + + private static PacketChunkReceiver getOrCreate(@NotNull UUID uuid, int chunkCount, ResourceLocation origin) { + return active.stream() + .filter(r -> r.uuid.equals(uuid)) + .findFirst() + .orElse(newReceiver(uuid, chunkCount, origin)); + } + + public static PacketChunkReceiver get(@NotNull UUID uuid) { + return active.stream().filter(r -> r.uuid.equals(uuid)).findFirst().orElse(null); + } + + private PacketChunkReceiver(@NotNull UUID uuid, int chunkCount, @Nullable DataHandlerDescriptor descriptor) { + this.uuid = uuid; + this.chunkCount = chunkCount; + networkedBuf = PacketByteBufs.create(); + this.descriptor = descriptor; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PacketChunkReceiver)) return false; + PacketChunkReceiver that = (PacketChunkReceiver) o; + return uuid.equals(that.uuid); + } + + @Override + public int hashCode() { + return Objects.hash(uuid); + } + + public boolean testFinished() { + ProgressListener listener = ChunkerProgress.getProgressListener(); + if (listener != null) { + listener.progressStagePercentage((100 * receivedCount) / chunkCount); + } + if (incomingBuffer == null) { + return true; + } + if (lastReadSerial >= chunkCount - 1) { + onFinish(); + return true; + } + return false; + } + + private void addBuffer(FriendlyByteBuf input) { + final int size = input.readableBytes(); + final int cap = networkedBuf.capacity() - networkedBuf.writerIndex(); + + if (cap < size) { + networkedBuf.capacity(networkedBuf.writerIndex() + size); + } + input.readBytes(networkedBuf, size); + input.clear(); + } + + protected void onFinish() { + incomingBuffer.clear(); + incomingBuffer = null; + + final BaseDataHandler baseHandler = descriptor.INSTANCE.get(); + if (baseHandler instanceof DataHandler.FromServer handler) { + handler.receiveFromMemory(networkedBuf); + } + } + + Map incomingBuffer = new HashMap<>(); + int lastReadSerial = -1; + int receivedCount = 0; + + public void processReceived(FriendlyByteBuf buf, int serialNo, int size) { + receivedCount++; + + if (lastReadSerial == serialNo - 1) { + addBuffer(buf); + lastReadSerial = serialNo; + } else { + //not sure if order is guaranteed by the underlying system! + boolean haveAll = true; + for (int nr = lastReadSerial + 1; nr < serialNo - 1; nr++) { + if (incomingBuffer.get(nr) == null) { + haveAll = false; + break; + } + } + + if (haveAll) { + for (int nr = lastReadSerial + 1; nr < serialNo - 1; nr++) { + addBuffer(incomingBuffer.get(nr)); + incomingBuffer.put(nr, null); + } + addBuffer(buf); + lastReadSerial = serialNo; + } else { + incomingBuffer.put(serialNo, buf); + } + } + } + } + + /** + * Responsible for splitting an outgoing ByteBuffer into several smaller Chunks and + * send them as seperate messages to the {@link Chunker}-Channel + */ + public static class PacketChunkSender { + private final FriendlyByteBuf networkedBuf; + public final UUID uuid; + public final int chunkCount; + public final int size; + public final ResourceLocation origin; + + public PacketChunkSender(FriendlyByteBuf buf, ResourceLocation origin) { + networkedBuf = buf; + + size = buf.readableBytes(); + chunkCount = (int) Math.ceil((double) size / MAX_PAYLOAD_SIZE); + uuid = UUID.randomUUID(); + this.origin = origin; + } + + public void sendChunks(Collection players) { + BCLib.LOGGER.info("Sending Request in " + chunkCount + " Packet-Chunks"); + for (int i = -1; i < chunkCount; i++) { + Chunker c = new Chunker(i, uuid, networkedBuf, chunkCount, origin); + FriendlyByteBuf buf = PacketByteBufs.create(); + c.serializeDataOnServer(buf); + + for (ServerPlayer player : players) { + ServerPlayNetworking.send(player, DESCRIPTOR.IDENTIFIER, buf); + } + } + } + } + + //header = version + UUID + serialNo + size, see serializeDataOnServer + private static final int HEADER_SIZE = 1 + 16 + 4 + 4; + + public static final int MAX_PACKET_SIZE = 1024 * 1024; + private static final int MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - HEADER_SIZE; + + public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, + "chunker"), + Chunker::new, + false, + false); + + private int serialNo; + private UUID uuid; + private int chunkCount; + private FriendlyByteBuf networkedBuf; + private ResourceLocation origin; + + protected Chunker(int serialNo, UUID uuid, FriendlyByteBuf networkedBuf, int chunkCount, ResourceLocation origin) { + super(DESCRIPTOR.IDENTIFIER); + this.serialNo = serialNo; + this.uuid = uuid; + this.networkedBuf = networkedBuf; + this.chunkCount = chunkCount; + this.origin = origin; + } + + protected Chunker() { + super(DESCRIPTOR.IDENTIFIER); + } + + + @Override + protected void serializeDataOnServer(FriendlyByteBuf buf) { + //Sending Header. Make sure to change HEADER_SIZE if you change this! + buf.writeByte(0); + buf.writeLong(uuid.getMostSignificantBits()); + buf.writeLong(uuid.getLeastSignificantBits()); + buf.writeInt(serialNo); + + //sending Payload + if (serialNo == -1) { + //this is our header-Chunk that transports status information + buf.writeInt(chunkCount); + writeString(buf, origin.getNamespace()); + writeString(buf, origin.getPath()); + } else { + //this is an actual payload chunk + buf.capacity(MAX_PACKET_SIZE); + final int size = Math.min(MAX_PAYLOAD_SIZE, networkedBuf.readableBytes()); + buf.writeInt(size); + networkedBuf.readBytes(buf, size); + } + } + + private PacketChunkReceiver receiver; + + @Override + protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { + final int version = buf.readByte(); + uuid = new UUID(buf.readLong(), buf.readLong()); + serialNo = buf.readInt(); + + if (serialNo == -1) { + chunkCount = buf.readInt(); + final String namespace = readString(buf); + final String path = readString(buf); + ResourceLocation ident = new ResourceLocation(namespace, path); + BCLib.LOGGER.info("Receiving " + chunkCount + " + Packet-Chunks for " + ident); + + receiver = PacketChunkReceiver.getOrCreate(uuid, chunkCount, ident); + } else { + receiver = PacketChunkReceiver.get(uuid); + if (receiver != null) { + final int size = buf.readInt(); + receiver.processReceived(buf, serialNo, size); + } else { + BCLib.LOGGER.error("Unknown Packet-Chunk Transfer for " + uuid); + } + } + } + + @Override + protected void runOnClientGameThread(Minecraft client) { + if (receiver != null) { + receiver.testFinished(); + } + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/ChunkerProgress.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/ChunkerProgress.java new file mode 100644 index 00000000..43d6696e --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/ChunkerProgress.java @@ -0,0 +1,28 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.util.ProgressListener; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.screens.ProgressScreen; + +@Environment(EnvType.CLIENT) +public class ChunkerProgress { + private static ProgressScreen progressScreen; + + @Environment(EnvType.CLIENT) + public static void setProgressScreen(ProgressScreen scr) { + progressScreen = scr; + } + + @Environment(EnvType.CLIENT) + public static ProgressScreen getProgressScreen() { + return progressScreen; + } + + @Environment(EnvType.CLIENT) + public static ProgressListener getProgressListener() { + return progressScreen; + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/FileContentWrapper.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/FileContentWrapper.java new file mode 100644 index 00000000..2a2c835d --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/FileContentWrapper.java @@ -0,0 +1,75 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import org.betterx.bclib.BCLib; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class FileContentWrapper { + private byte[] rawContent; + private ByteArrayOutputStream outputStream; + + FileContentWrapper(byte[] content) { + this.rawContent = content; + this.outputStream = null; + } + + public byte[] getOriginalContent() { + return rawContent; + } + + public byte[] getRawContent() { + if (outputStream != null) { + return outputStream.toByteArray(); + } + return rawContent; + } + + private void invalidateOutputStream() { + if (this.outputStream != null) { + try { + this.outputStream.close(); + } catch (IOException e) { + BCLib.LOGGER.debug(e); + } + } + this.outputStream = null; + } + + public void setRawContent(byte[] rawContent) { + this.rawContent = rawContent; + invalidateOutputStream(); + } + + public void syncWithOutputStream() { + if (outputStream != null) { + try { + outputStream.flush(); + } catch (IOException e) { + BCLib.LOGGER.error(e.getMessage()); + e.printStackTrace(); + } + setRawContent(getRawContent()); + invalidateOutputStream(); + } + } + + public ByteArrayInputStream getInputStream() { + if (rawContent == null) return new ByteArrayInputStream(new byte[0]); + return new ByteArrayInputStream(rawContent); + } + + public ByteArrayOutputStream getOrCreateOutputStream() { + if (this.outputStream == null) { + return this.getEmptyOutputStream(); + } + return this.outputStream; + } + + public ByteArrayOutputStream getEmptyOutputStream() { + invalidateOutputStream(); + this.outputStream = new ByteArrayOutputStream(this.rawContent.length); + return this.outputStream; + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/HelloClient.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/HelloClient.java new file mode 100644 index 00000000..48397b67 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/HelloClient.java @@ -0,0 +1,509 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.loader.api.metadata.ModEnvironment; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.DataHandlerDescriptor; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.config.ServerConfig; +import org.betterx.bclib.gui.screens.ModListScreen; +import org.betterx.bclib.gui.screens.ProgressScreen; +import org.betterx.bclib.gui.screens.SyncFilesScreen; +import org.betterx.bclib.gui.screens.WarnBCLibVersionMismatch; +import org.betterx.bclib.util.ModUtil; +import org.betterx.bclib.util.ModUtil.ModInfo; +import org.betterx.bclib.util.PathUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +/** + * Sent from the Server to the Client. + *

+ * For Details refer to {@link HelloServer} + */ +public class HelloClient extends DataHandler.FromServer { + public record OfferedModInfo(String version, int size, boolean canDownload) { + } + + public interface IServerModMap extends Map { + } + + public static class ServerModMap extends HashMap implements IServerModMap { + } + + public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, + "hello_client"), + HelloClient::new, + false, + false); + + public HelloClient() { + super(DESCRIPTOR.IDENTIFIER); + } + + static String getBCLibVersion() { + return ModUtil.getModVersion(BCLib.MOD_ID); + } + + @Override + protected boolean prepareDataOnServer() { + if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the server."); + return false; + } + + AutoSync.loadSyncFolder(); + return true; + } + + @Override + protected void serializeDataOnServer(FriendlyByteBuf buf) { + final String vbclib = getBCLibVersion(); + BCLib.LOGGER.info("Sending Hello to Client. (server=" + vbclib + ")"); + + //write BCLibVersion (=protocol version) + buf.writeInt(ModUtil.convertModVersion(vbclib)); + + if (Configs.SERVER_CONFIG.isOfferingMods() || Configs.SERVER_CONFIG.isOfferingInfosForMods()) { + List mods = DataExchangeAPI.registeredMods(); + final List inmods = mods; + if (Configs.SERVER_CONFIG.isOfferingAllMods() || Configs.SERVER_CONFIG.isOfferingInfosForMods()) { + mods = new ArrayList<>(inmods.size()); + mods.addAll(inmods); + mods.addAll(ModUtil + .getMods() + .entrySet() + .stream() + .filter(entry -> entry.getValue().metadata.getEnvironment() != ModEnvironment.SERVER && !inmods.contains( + entry.getKey())) + .map(entry -> entry.getKey()) + .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()); + for (String modID : mods) { + final String ver = ModUtil.getModVersion(modID); + int size = 0; + + final ModInfo mi = ModUtil.getModInfo(modID); + if (mi != null) { + try { + size = (int) Files.size(mi.jarPath); + } catch (IOException e) { + BCLib.LOGGER.error("Unable to get File Size: " + e.getMessage()); + } + } + + + writeString(buf, modID); + buf.writeInt(ModUtil.convertModVersion(ver)); + buf.writeInt(size); + final boolean canDownload = size > 0 && Configs.SERVER_CONFIG.isOfferingMods() && (Configs.SERVER_CONFIG.isOfferingAllMods() || inmods.contains( + modID)); + buf.writeBoolean(canDownload); + + BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver + " (size: " + PathUtil.humanReadableFileSize( + size) + ", download=" + canDownload + ")"); + } + } else { + BCLib.LOGGER.info("Server will not list Mods."); + buf.writeInt(0); + } + + if (Configs.SERVER_CONFIG.isOfferingFiles() || Configs.SERVER_CONFIG.isOfferingConfigs()) { + //do only include files that exist on the server + final List existingAutoSyncFiles = AutoSync.getAutoSyncFiles() + .stream() + .filter(e -> e.fileName.exists()) + .filter(e -> (e.isConfigFile() && Configs.SERVER_CONFIG.isOfferingConfigs()) || (e instanceof AutoFileSyncEntry.ForDirectFileRequest && Configs.SERVER_CONFIG.isOfferingFiles())) + .collect(Collectors.toList()); + + //send config Data + buf.writeInt(existingAutoSyncFiles.size()); + for (AutoFileSyncEntry entry : existingAutoSyncFiles) { + entry.serialize(buf); + BCLib.LOGGER.info(" - Offering " + (entry.isConfigFile() ? "Config " : "File ") + entry); + } + } else { + BCLib.LOGGER.info("Server will neither offer Files nor Configs."); + buf.writeInt(0); + } + + if (Configs.SERVER_CONFIG.isOfferingFiles()) { + buf.writeInt(AutoSync.syncFolderDescriptions.size()); + AutoSync.syncFolderDescriptions.forEach(desc -> { + BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete=" + desc.removeAdditionalFiles + ")"); + desc.serialize(buf); + }); + } else { + BCLib.LOGGER.info("Server will not offer Sync Folders."); + buf.writeInt(0); + } + + buf.writeBoolean(Configs.SERVER_CONFIG.isOfferingInfosForMods()); + } + + String bclibVersion = "0.0.0"; + + + IServerModMap modVersion = new ServerModMap(); + List autoSyncedFiles = null; + List autoSynFolders = null; + boolean serverPublishedModInfo = false; + + @Environment(EnvType.CLIENT) + @Override + protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { + //read BCLibVersion (=protocol version) + bclibVersion = ModUtil.convertModVersion(buf.readInt()); + + //read Plugin Versions + modVersion = new ServerModMap(); + int count = buf.readInt(); + for (int i = 0; i < count; i++) { + final String id = readString(buf); + final String version = ModUtil.convertModVersion(buf.readInt()); + final int size; + final boolean canDownload; + //since v0.4.1 we also send the size of the mod-File + size = buf.readInt(); + canDownload = buf.readBoolean(); + modVersion.put(id, new OfferedModInfo(version, size, canDownload)); + } + + //read config Data + count = buf.readInt(); + autoSyncedFiles = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + //System.out.println("Deserializing "); + AutoSync.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf); + autoSyncedFiles.add(t); + //System.out.println(t.first); + } + + + autoSynFolders = new ArrayList<>(1); + //since v0.4.1 we also send the sync folders + final int folderCount = buf.readInt(); + for (int i = 0; i < folderCount; i++) { + SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf); + autoSynFolders.add(desc); + } + + serverPublishedModInfo = buf.readBoolean(); + } + + @Environment(EnvType.CLIENT) + private void processAutoSyncFolder(final List filesToRequest, + final List filesToRemove) { + if (!Configs.CLIENT_CONFIG.isAcceptingFiles()) { + return; + } + + if (autoSynFolders.size() > 0) { + BCLib.LOGGER.info("Folders offered by Server:"); + } + + autoSynFolders.forEach(desc -> { + //desc contains the fileCache sent from the server, load the local version to get hold of the actual file cache on the client + SyncFolderDescriptor localDescriptor = AutoSync.getSyncFolderDescriptor(desc.folderID); + if (localDescriptor != null) { + BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")"); + localDescriptor.invalidateCache(); + + desc.relativeFilesStream() + .filter(desc::discardChildElements) + .forEach(subFile -> { + BCLib.LOGGER.warning(" * " + subFile.relPath + " (REJECTED)"); + }); + + + if (desc.removeAdditionalFiles) { + List additionalFiles = localDescriptor.relativeFilesStream() + .filter(subFile -> !desc.hasRelativeFile( + subFile)) + .map(desc::mapAbsolute) + .filter(desc::acceptChildElements) + .map(absPath -> new AutoSyncID.ForDirectFileRequest( + desc.folderID, + absPath.toFile())) + .collect(Collectors.toList()); + + additionalFiles.forEach(aid -> BCLib.LOGGER.info(" * " + desc.localFolder.relativize(aid.relFile.toPath()) + " (missing on server)")); + filesToRemove.addAll(additionalFiles); + } + + desc.relativeFilesStream() + .filter(desc::acceptChildElements) + .forEach(subFile -> { + SyncFolderDescriptor.SubFile localSubFile = localDescriptor.getLocalSubFile(subFile.relPath); + if (localSubFile != null) { + //the file exists locally, check if the hashes match + if (!localSubFile.hash.equals(subFile.hash)) { + BCLib.LOGGER.info(" * " + subFile.relPath + " (changed)"); + filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, + new File(subFile.relPath))); + } else { + BCLib.LOGGER.info(" * " + subFile.relPath); + } + } else { + //the file is missing locally + BCLib.LOGGER.info(" * " + subFile.relPath + " (missing on client)"); + filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, + new File(subFile.relPath))); + } + }); + + //free some memory + localDescriptor.invalidateCache(); + } else { + BCLib.LOGGER.info(" - " + desc.folderID + " (Failed to find)"); + } + }); + } + + @Environment(EnvType.CLIENT) + private void processSingleFileSync(final List filesToRequest) { + final boolean debugHashes = Configs.CLIENT_CONFIG.shouldPrintDebugHashes(); + + if (autoSyncedFiles.size() > 0) { + BCLib.LOGGER.info("Files offered by Server:"); + } + + //Handle single sync files + //Single files need to be registered for sync on both client and server + //There are no restrictions to the target folder, but the client decides the final + //location. + for (AutoSync.AutoSyncTriple e : autoSyncedFiles) { + String actionString = ""; + FileContentWrapper contentWrapper = new FileContentWrapper(e.serverContent); + if (e.localMatch == null) { + actionString = "(unknown source -> omitting)"; + //filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID)); + } else if (e.localMatch.needTransfer.test(e.localMatch.getFileHash(), e.serverHash, contentWrapper)) { + actionString = "(prepare update)"; + //we did not yet receive the new content + if (contentWrapper.getRawContent() == null) { + filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID)); + } else { + filesToRequest.add(new AutoSyncID.WithContentOverride(e.serverHash.modID, + e.serverHash.uniqueID, + contentWrapper, + e.localMatch.fileName)); + } + } + + BCLib.LOGGER.info(" - " + e + ": " + actionString); + if (debugHashes) { + BCLib.LOGGER.info(" * " + e.serverHash + " (Server)"); + BCLib.LOGGER.info(" * " + e.localMatch.getFileHash() + " (Client)"); + BCLib.LOGGER.info(" * local Content " + (contentWrapper.getRawContent() == null)); + } + } + } + + + @Environment(EnvType.CLIENT) + private void processModFileSync(final List filesToRequest, final Set mismatchingMods) { + for (Entry e : modVersion.entrySet()) { + final String localVersion = ModUtil.convertModVersion(ModUtil.convertModVersion(ModUtil.getModVersion(e.getKey()))); + final OfferedModInfo serverInfo = e.getValue(); + + ModInfo nfo = ModUtil.getModInfo(e.getKey()); + final boolean clientOnly = nfo != null && nfo.metadata.getEnvironment() == ModEnvironment.CLIENT; + final boolean requestMod = !clientOnly && !serverInfo.version.equals(localVersion) && serverInfo.size > 0 && serverInfo.canDownload; + + BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.version + ", size=" + PathUtil.humanReadableFileSize( + serverInfo.size) + (requestMod ? ", requesting" : "") + (serverInfo.canDownload + ? "" + : ", not offered") + (clientOnly ? ", client only" : "") + ")"); + if (requestMod) { + filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverInfo.version)); + } + if (!serverInfo.version.equals(localVersion)) { + mismatchingMods.add(e.getKey()); + } + } + + mismatchingMods.addAll(ModListScreen.localMissing(modVersion)); + mismatchingMods.addAll(ModListScreen.serverMissing(modVersion)); + } + + @Override + protected boolean isBlocking() { + return true; + } + + @Environment(EnvType.CLIENT) + @Override + protected void runOnClientGameThread(Minecraft client) { + if (!Configs.CLIENT_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the client."); + return; + } + final String localBclibVersion = getBCLibVersion(); + BCLib.LOGGER.info("Received Hello from Server. (client=" + localBclibVersion + ", server=" + bclibVersion + ")"); + + if (ModUtil.convertModVersion(localBclibVersion) != ModUtil.convertModVersion(bclibVersion)) { + showBCLibError(client); + return; + } + + final List filesToRequest = new ArrayList<>(2); + final List filesToRemove = new ArrayList<>(2); + final Set mismatchingMods = new HashSet<>(2); + + + processModFileSync(filesToRequest, mismatchingMods); + processSingleFileSync(filesToRequest); + processAutoSyncFolder(filesToRequest, filesToRemove); + + //Handle folder sync + //Both client and server need to know about the folder you want to sync + //Files can only get placed within that folder + + if ((filesToRequest.size() > 0 || filesToRemove.size() > 0) && (Configs.CLIENT_CONFIG.isAcceptingMods() || Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles())) { + showSyncFilesScreen(client, filesToRequest, filesToRemove); + return; + } else if (serverPublishedModInfo && mismatchingMods.size() > 0 && Configs.CLIENT_CONFIG.isShowingModInfo()) { + client.setScreen(new ModListScreen(client.screen, + Component.translatable("title.bclib.modmissmatch"), + Component.translatable("message.bclib.modmissmatch"), + CommonComponents.GUI_PROCEED, + ModUtil.getMods(), + modVersion)); + return; + } + } + + @Environment(EnvType.CLIENT) + protected void showBCLibError(Minecraft client) { + BCLib.LOGGER.error("BCLib differs on client and server."); + client.setScreen(new WarnBCLibVersionMismatch((download) -> { + if (download) { + requestBCLibDownload(); + + this.onCloseSyncFilesScreen(); + } else { + Minecraft.getInstance() + .setScreen(null); + } + })); + } + + @Environment(EnvType.CLIENT) + protected void showSyncFilesScreen(Minecraft client, + List files, + final List filesToRemove) { + int configFiles = 0; + int singleFiles = 0; + int folderFiles = 0; + int modFiles = 0; + + for (AutoSyncID aid : files) { + if (aid.isConfigFile()) { + configFiles++; + } else if (aid instanceof AutoSyncID.ForModFileRequest) { + modFiles++; + } else if (aid instanceof AutoSyncID.ForDirectFileRequest) { + folderFiles++; + } else { + singleFiles++; + } + } + + client.setScreen(new SyncFilesScreen(modFiles, + configFiles, + singleFiles, + folderFiles, + filesToRemove.size(), + modVersion, + (downloadMods, downloadConfigs, downloadFiles, removeFiles) -> { + if (downloadMods || downloadConfigs || downloadFiles) { + BCLib.LOGGER.info("Updating local Files:"); + List localChanges = new ArrayList<>( + files.toArray().length); + List requestFiles = new ArrayList<>(files.toArray().length); + + files.forEach(aid -> { + if (aid.isConfigFile() && downloadConfigs) { + processOfferedFile(requestFiles, aid); + } else if (aid instanceof AutoSyncID.ForModFileRequest && downloadMods) { + processOfferedFile(requestFiles, aid); + } else if (downloadFiles) { + processOfferedFile(requestFiles, aid); + } + }); + + requestFileDownloads(requestFiles); + } + if (removeFiles) { + filesToRemove.forEach(aid -> { + BCLib.LOGGER.info(" - " + aid.relFile + " (removing)"); + aid.relFile.delete(); + }); + } + + this.onCloseSyncFilesScreen(); + })); + } + + @Environment(EnvType.CLIENT) + private void onCloseSyncFilesScreen() { + Minecraft.getInstance() + .setScreen(ChunkerProgress.getProgressScreen()); + } + + private void processOfferedFile(List requestFiles, AutoSyncID aid) { + if (aid instanceof AutoSyncID.WithContentOverride) { + final AutoSyncID.WithContentOverride aidc = (AutoSyncID.WithContentOverride) aid; + BCLib.LOGGER.info(" - " + aid + " (updating Content)"); + + SendFiles.writeSyncedFile(aid, aidc.contentWrapper.getRawContent(), aidc.localFile); + } else { + requestFiles.add(aid); + BCLib.LOGGER.info(" - " + aid + " (requesting)"); + } + } + + private void requestBCLibDownload() { + BCLib.LOGGER.warning("Starting download of BCLib"); + requestFileDownloads(List.of(new AutoSyncID.ForModFileRequest(BCLib.MOD_ID, bclibVersion))); + } + + @Environment(EnvType.CLIENT) + private void requestFileDownloads(List files) { + BCLib.LOGGER.info("Starting download of Files:" + files.size()); + + final ProgressScreen progress = new ProgressScreen(null, + Component.translatable("title.bclib.filesync.progress"), + Component.translatable("message.bclib.filesync.progress")); + progress.progressStart(Component.translatable("message.bclib.filesync.progress.stage.empty")); + ChunkerProgress.setProgressScreen(progress); + + DataExchangeAPI.send(new RequestFiles(files)); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/HelloServer.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/HelloServer.java new file mode 100644 index 00000000..77b958b2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/HelloServer.java @@ -0,0 +1,115 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.player.Player; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.DataHandlerDescriptor; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.util.ModUtil; + +import java.io.File; + +/** + * This message is sent once a player enters the world. It initiates a sequence of Messages that will sync files between both + * client and server. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Description
ServerClient
Player enters World
<--{@link HelloServer}Sends the current BLib-Version installed on the Client
{@link HelloClient}-->Sends the current BClIb-Version, the Version of all Plugins and data for all AutpoSync-Files + * ({@link DataExchangeAPI#addAutoSyncFile(String, File)} on the Server
<--{@link RequestFiles}Request missing or out of sync Files from the Server
{@link SendFiles}-->Send Files from the Server to the Client
+ */ +public class HelloServer extends DataHandler.FromClient { + public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, + "hello_server"), + HelloServer::new, + true, + false); + + protected String bclibVersion = "0.0.0"; + + public HelloServer() { + super(DESCRIPTOR.IDENTIFIER); + } + + @Environment(EnvType.CLIENT) + @Override + protected boolean prepareDataOnClient() { + if (!Configs.CLIENT_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the client."); + return false; + } + + return true; + } + + @Environment(EnvType.CLIENT) + @Override + protected void serializeDataOnClient(FriendlyByteBuf buf) { + BCLib.LOGGER.info("Sending hello to server."); + buf.writeInt(ModUtil.convertModVersion(HelloClient.getBCLibVersion())); + } + + @Override + protected void deserializeIncomingDataOnServer(FriendlyByteBuf buf, Player player, PacketSender responseSender) { + bclibVersion = ModUtil.convertModVersion(buf.readInt()); + } + + @Override + protected void runOnServerGameThread(MinecraftServer server, Player player) { + if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the server."); + return; + } + + String localBclibVersion = HelloClient.getBCLibVersion(); + BCLib.LOGGER.info("Received Hello from Client. (server=" + localBclibVersion + ", client=" + bclibVersion + ")"); + + if (!server.isPublished()) { + BCLib.LOGGER.info("Auto-Sync is disabled for Singleplayer worlds."); + return; + } + + reply(new HelloClient(), server); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/RequestFiles.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/RequestFiles.java new file mode 100644 index 00000000..25e8169d --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/RequestFiles.java @@ -0,0 +1,105 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.player.Player; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.DataHandlerDescriptor; +import org.betterx.bclib.config.Configs; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class RequestFiles extends DataHandler.FromClient { + public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, + "request_files"), + RequestFiles::new, + false, + false); + static String currentToken = ""; + + protected List files; + + private RequestFiles() { + this(null); + } + + public RequestFiles(List files) { + super(DESCRIPTOR.IDENTIFIER); + this.files = files; + } + + @Environment(EnvType.CLIENT) + @Override + protected boolean prepareDataOnClient() { + if (!Configs.CLIENT_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the client."); + return false; + } + return true; + } + + @Environment(EnvType.CLIENT) + @Override + protected void serializeDataOnClient(FriendlyByteBuf buf) { + newToken(); + writeString(buf, currentToken); + + buf.writeInt(files.size()); + + for (AutoSyncID a : files) { + a.serializeData(buf); + } + } + + String receivedToken = ""; + + @Override + protected void deserializeIncomingDataOnServer(FriendlyByteBuf buf, Player player, PacketSender responseSender) { + receivedToken = readString(buf); + int size = buf.readInt(); + files = new ArrayList<>(size); + + BCLib.LOGGER.info("Client requested " + size + " Files:"); + for (int i = 0; i < size; i++) { + AutoSyncID asid = AutoSyncID.deserializeData(buf); + files.add(asid); + BCLib.LOGGER.info(" - " + asid); + } + + + } + + @Override + protected void runOnServerGameThread(MinecraftServer server, Player player) { + if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the server."); + return; + } + + List syncEntries = files.stream() + .map(asid -> AutoFileSyncEntry.findMatching(asid)) + .filter(e -> e != null) + .collect(Collectors.toList()); + + reply(new SendFiles(syncEntries, receivedToken), server); + } + + public static void newToken() { + currentToken = UUID.randomUUID() + .toString(); + } + + static { + newToken(); + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/SendFiles.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/SendFiles.java new file mode 100644 index 00000000..549698f7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/SendFiles.java @@ -0,0 +1,219 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.DataHandlerDescriptor; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.gui.screens.ConfirmRestartScreen; +import org.betterx.bclib.util.Pair; +import org.betterx.bclib.util.PathUtil; +import org.betterx.bclib.util.Triple; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class SendFiles extends DataHandler.FromServer { + public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, + "send_files"), + SendFiles::new, + false, + false); + + protected List files; + private String token; + + public SendFiles() { + this(null, ""); + } + + public SendFiles(List files, String token) { + super(DESCRIPTOR.IDENTIFIER); + this.files = files; + this.token = token; + } + + @Override + protected boolean prepareDataOnServer() { + if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { + BCLib.LOGGER.info("Auto-Sync was disabled on the server."); + return false; + } + + return true; + } + + @Override + protected void serializeDataOnServer(FriendlyByteBuf buf) { + List existingFiles = files.stream() + .filter(e -> e != null && e.fileName != null && e.fileName.exists()) + .collect(Collectors.toList()); + /* + //this will try to send a file that was not registered or requested by the client + existingFiles.add(new AutoFileSyncEntry("none", new File("D:\\MinecraftPlugins\\BetterNether\\run\\server.properties"),true,(a, b, content) -> { + System.out.println("Got Content:" + content.length); + return true; + }));*/ + + /*//this will try to send a folder-file that was not registered or requested by the client + existingFiles.add(new AutoFileSyncEntry.ForDirectFileRequest(DataExchange.SYNC_FOLDER.folderID, new File("test.json"), DataExchange.SYNC_FOLDER.mapAbsolute("test.json").toFile()));*/ + + /*//this will try to send a folder-file that was not registered or requested by the client and is outside the base-folder + existingFiles.add(new AutoFileSyncEntry.ForDirectFileRequest(DataExchange.SYNC_FOLDER.folderID, new File("../breakout.json"), DataExchange.SYNC_FOLDER.mapAbsolute("../breakout.json").toFile()));*/ + + + writeString(buf, token); + buf.writeInt(existingFiles.size()); + + BCLib.LOGGER.info("Sending " + existingFiles.size() + " Files to Client:"); + for (AutoFileSyncEntry entry : existingFiles) { + int length = entry.serializeContent(buf); + BCLib.LOGGER.info(" - " + entry + " (" + PathUtil.humanReadableFileSize(length) + ")"); + } + } + + private List> receivedFiles; + + @Environment(EnvType.CLIENT) + @Override + protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { + if (Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles() || Configs.CLIENT_CONFIG.isAcceptingMods()) { + token = readString(buf); + if (!token.equals(RequestFiles.currentToken)) { + RequestFiles.newToken(); + BCLib.LOGGER.error("Unrequested File Transfer!"); + receivedFiles = new ArrayList<>(0); + return; + } + RequestFiles.newToken(); + + int size = buf.readInt(); + receivedFiles = new ArrayList<>(size); + BCLib.LOGGER.info("Server sent " + size + " Files:"); + for (int i = 0; i < size; i++) { + Triple p = AutoFileSyncEntry.deserializeContent(buf); + if (p.first != null) { + final String type; + if (p.first.isConfigFile() && Configs.CLIENT_CONFIG.isAcceptingConfigs()) { + receivedFiles.add(p); + type = "Accepted Config "; + } else if (p.first instanceof AutoFileSyncEntry.ForModFileRequest && Configs.CLIENT_CONFIG.isAcceptingMods()) { + receivedFiles.add(p); + type = "Accepted Mod "; + } else if (Configs.CLIENT_CONFIG.isAcceptingFiles()) { + receivedFiles.add(p); + type = "Accepted File "; + } else { + type = "Ignoring "; + } + BCLib.LOGGER.info(" - " + type + p.first + " (" + PathUtil.humanReadableFileSize(p.second.length) + ")"); + } else { + BCLib.LOGGER.error(" - Failed to receive File " + p.third + ", possibly sent from a Mod that is not installed on the client."); + } + } + } + } + + @Environment(EnvType.CLIENT) + @Override + protected void runOnClientGameThread(Minecraft client) { + if (Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles() || Configs.CLIENT_CONFIG.isAcceptingMods()) { + BCLib.LOGGER.info("Writing Files:"); + + for (Pair entry : receivedFiles) { + final AutoFileSyncEntry e = entry.first; + final byte[] data = entry.second; + + writeSyncedFile(e, data, e.fileName); + } + + showConfirmRestart(client); + } + } + + + @Environment(EnvType.CLIENT) + static void writeSyncedFile(AutoSyncID e, byte[] data, File fileName) { + if (fileName != null && !PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())) { + BCLib.LOGGER.error(fileName + " is not within game folder " + PathUtil.GAME_FOLDER); + return; + } + + if (!PathUtil.MOD_BAK_FOLDER.toFile().exists()) { + PathUtil.MOD_BAK_FOLDER.toFile().mkdirs(); + } + + Path path = fileName != null ? fileName.toPath() : null; + Path removeAfter = null; + if (e instanceof AutoFileSyncEntry.ForModFileRequest mase) { + removeAfter = path; + int count = 0; + final String prefix = "_bclib_synced_"; + String name = prefix + mase.modID + "_" + mase.version.replace(".", "_") + ".jar"; + do { + if (path != null) { + //move to the same directory as the existing Mod + path = path.getParent() + .resolve(name); + } else { + //move to the default mode location + path = PathUtil.MOD_FOLDER.resolve(name); + } + count++; + name = prefix + mase.modID + "_" + mase.version.replace(".", "_") + "__" + String.format("%03d", + count) + ".jar"; + } while (path.toFile().exists()); + } + + BCLib.LOGGER.info(" - Writing " + path + " (" + PathUtil.humanReadableFileSize(data.length) + ")"); + try { + final File parentFile = path.getParent() + .toFile(); + if (!parentFile.exists()) { + parentFile.mkdirs(); + } + Files.write(path, data); + if (removeAfter != null) { + final String bakFileName = removeAfter.toFile().getName(); + String collisionFreeName = bakFileName; + Path targetPath; + int count = 0; + do { + targetPath = PathUtil.MOD_BAK_FOLDER.resolve(collisionFreeName); + count++; + collisionFreeName = String.format("%03d", count) + "_" + bakFileName; + } while (targetPath.toFile().exists()); + + BCLib.LOGGER.info(" - Moving " + removeAfter + " to " + targetPath); + removeAfter.toFile().renameTo(targetPath.toFile()); + } + AutoSync.didReceiveFile(e, fileName); + + + } catch (IOException ioException) { + BCLib.LOGGER.error(" --> Writing " + fileName + " failed: " + ioException); + } + } + + @Environment(EnvType.CLIENT) + protected void showConfirmRestart(Minecraft client) { + client.setScreen(new ConfirmRestartScreen(() -> { + Minecraft.getInstance() + .setScreen(null); + client.stop(); + })); + + } +} diff --git a/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/SyncFolderDescriptor.java b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/SyncFolderDescriptor.java new file mode 100644 index 00000000..da0a8b73 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/dataexchange/handler/autosync/SyncFolderDescriptor.java @@ -0,0 +1,214 @@ +package org.betterx.bclib.api.dataexchange.handler.autosync; + +import net.minecraft.network.FriendlyByteBuf; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataHandler; +import org.betterx.bclib.api.dataexchange.FileHash; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSyncID.ForDirectFileRequest; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.util.PathUtil; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.jetbrains.annotations.NotNull; + +public class SyncFolderDescriptor { + static class SubFile { + public final String relPath; + public final FileHash hash; + + + SubFile(String relPath, FileHash hash) { + this.relPath = relPath; + this.hash = hash; + } + + @Override + public String toString() { + return relPath; + } + + public void serialize(FriendlyByteBuf buf) { + DataHandler.writeString(buf, relPath); + hash.serialize(buf); + } + + public static SubFile deserialize(FriendlyByteBuf buf) { + final String relPath = DataHandler.readString(buf); + FileHash hash = FileHash.deserialize(buf); + return new SubFile(relPath, hash); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof String) return relPath.equals(o); + if (!(o instanceof SubFile)) return false; + SubFile subFile = (SubFile) o; + return relPath.equals(subFile.relPath); + } + + @Override + public int hashCode() { + return relPath.hashCode(); + } + } + + @NotNull + public final String folderID; + public final boolean removeAdditionalFiles; + @NotNull + public final Path localFolder; + + private List fileCache; + + public SyncFolderDescriptor(String folderID, Path localFolder, boolean removeAdditionalFiles) { + this.removeAdditionalFiles = removeAdditionalFiles; + this.folderID = folderID; + this.localFolder = localFolder; + fileCache = null; + } + + @Override + public String toString() { + return "SyncFolderDescriptor{" + "folderID='" + folderID + '\'' + ", removeAdditionalFiles=" + removeAdditionalFiles + ", localFolder=" + localFolder + ", files=" + ( + fileCache == null + ? "?" + : fileCache.size()) + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof String) { + return folderID.equals(o); + } + if (o instanceof ForDirectFileRequest) { + return folderID.equals(((ForDirectFileRequest) o).uniqueID); + } + if (!(o instanceof SyncFolderDescriptor)) return false; + SyncFolderDescriptor that = (SyncFolderDescriptor) o; + return folderID.equals(that.folderID); + } + + @Override + public int hashCode() { + return folderID.hashCode(); + } + + public int fileCount() { + return fileCache == null ? 0 : fileCache.size(); + } + + public void invalidateCache() { + fileCache = null; + } + + public void loadCache() { + if (fileCache == null) { + fileCache = new ArrayList<>(8); + PathUtil.fileWalker(localFolder.toFile(), p -> fileCache.add(new SubFile(localFolder.relativize(p) + .toString(), + FileHash.create(p.toFile())))); + + /*//this tests if we can trick the system to load files that are not beneath the base-folder + if (!BCLib.isClient()) { + fileCache.add(new SubFile("../breakout.json", FileHash.create(mapAbsolute("../breakout.json").toFile()))); + }*/ + } + } + + public void serialize(FriendlyByteBuf buf) { + final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(AutoSync.SYNC_CATEGORY, "debugHashes", false); + loadCache(); + + DataHandler.writeString(buf, folderID); + buf.writeBoolean(removeAdditionalFiles); + buf.writeInt(fileCache.size()); + fileCache.forEach(fl -> { + BCLib.LOGGER.info(" - " + fl.relPath); + if (debugHashes) { + BCLib.LOGGER.info(" " + fl.hash); + } + fl.serialize(buf); + }); + } + + public static SyncFolderDescriptor deserialize(FriendlyByteBuf buf) { + final String folderID = DataHandler.readString(buf); + final boolean remAddFiles = buf.readBoolean(); + final int count = buf.readInt(); + SyncFolderDescriptor localDescriptor = AutoSync.getSyncFolderDescriptor(folderID); + + final SyncFolderDescriptor desc; + if (localDescriptor != null) { + desc = new SyncFolderDescriptor(folderID, + localDescriptor.localFolder, + localDescriptor.removeAdditionalFiles && remAddFiles); + desc.fileCache = new ArrayList<>(count); + } else { + BCLib.LOGGER.warning(BCLib.isClient() + ? "Client" + : "Server" + " does not know Sync-Folder ID '" + folderID + "'"); + desc = null; + } + + for (int i = 0; i < count; i++) { + SubFile relPath = SubFile.deserialize(buf); + if (desc != null) desc.fileCache.add(relPath); + } + + return desc; + } + + //Note: make sure loadCache was called before using this + boolean hasRelativeFile(String relFile) { + return fileCache.stream() + .filter(sf -> sf.equals(relFile)) + .findFirst() + .isPresent(); + } + + //Note: make sure loadCache was called before using this + boolean hasRelativeFile(SubFile subFile) { + return hasRelativeFile(subFile.relPath); + } + + //Note: make sure loadCache was called before using this + SubFile getLocalSubFile(String relPath) { + return fileCache.stream() + .filter(sf -> sf.relPath.equals(relPath)) + .findFirst() + .orElse(null); + } + + Stream relativeFilesStream() { + loadCache(); + return fileCache.stream(); + } + + public Path mapAbsolute(String relPath) { + return this.localFolder.resolve(relPath) + .normalize(); + } + + public Path mapAbsolute(SubFile subFile) { + return this.localFolder.resolve(subFile.relPath) + .normalize(); + } + + public boolean acceptChildElements(Path absPath) { + return PathUtil.isChildOf(this.localFolder, absPath); + } + + public boolean acceptChildElements(SubFile subFile) { + return acceptChildElements(mapAbsolute(subFile)); + } + + public boolean discardChildElements(SubFile subFile) { + return !acceptChildElements(subFile); + } +} diff --git a/src/main/java/org/betterx/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/org/betterx/bclib/api/datafixer/DataFixerAPI.java new file mode 100644 index 00000000..237b9ef4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/datafixer/DataFixerAPI.java @@ -0,0 +1,632 @@ +package org.betterx.bclib.api.datafixer; + +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.worldselection.EditWorldScreen; +import net.minecraft.nbt.*; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.storage.RegionFile; +import net.minecraft.world.level.storage.LevelResource; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.WorldDataAPI; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.gui.screens.AtomicProgressListener; +import org.betterx.bclib.gui.screens.ConfirmFixScreen; +import org.betterx.bclib.gui.screens.LevelFixErrorScreen; +import org.betterx.bclib.gui.screens.LevelFixErrorScreen.Listener; +import org.betterx.bclib.gui.screens.ProgressScreen; +import org.betterx.bclib.util.Logger; + +import java.io.*; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +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; +import org.jetbrains.annotations.NotNull; + +/** + * API to manage Patches that need to get applied to a world + */ +public class DataFixerAPI { + static final Logger LOGGER = new Logger("DataFixerAPI"); + + static class State { + public boolean didFail = false; + protected ArrayList 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 + public interface Callback { + void call(); + } + + private static boolean wrapCall(LevelStorageSource levelSource, + String levelID, + Function runWithLevel) { + LevelStorageSource.LevelStorageAccess levelStorageAccess; + try { + levelStorageAccess = levelSource.createAccess(levelID); + } catch (IOException e) { + BCLib.LOGGER.warning("Failed to read level {} data", levelID, e); + SystemToast.onWorldAccessFailure(Minecraft.getInstance(), levelID); + Minecraft.getInstance().setScreen(null); + return true; + } + + boolean returnValue = runWithLevel.apply(levelStorageAccess); + + try { + levelStorageAccess.close(); + } catch (IOException e) { + BCLib.LOGGER.warning("Failed to unlock access to level {}", levelID, e); + } + + return returnValue; + } + + /** + * Will apply necessary Patches to the world. + * + * @param levelSource The SourceStorage for this Minecraft instance, You can get this using + * {@code Minecraft.getInstance().getLevelSource()} + * @param levelID The ID of the Level you want to patch + * @param showUI {@code true}, if you want to present the user with a Screen that offers to backup the world + * before applying the patches + * @param onResume When this method retursn {@code true}, this function will be called when the world is ready + * @return {@code true} if the UI was displayed. The UI is only displayed if {@code showUI} was {@code true} and + * patches were enabled in the config and the Guardian did find any patches that need to be applied to the world. + */ + public static boolean fixData(LevelStorageSource levelSource, + String levelID, + boolean showUI, + Consumer onResume) { + return wrapCall(levelSource, levelID, (levelStorageAccess) -> fixData(levelStorageAccess, showUI, onResume)); + } + + /** + * Will apply necessary Patches to the world. + * + * @param levelStorageAccess The access class of the level you want to patch + * @param showUI {@code true}, if you want to present the user with a Screen that offers to backup the world + * before applying the patches + * @param onResume When this method retursn {@code true}, this function will be called when the world is ready + * @return {@code true} if the UI was displayed. The UI is only displayed if {@code showUI} was {@code true} and + * patches were enabled in the config and the Guardian did find any patches that need to be applied to the world. + */ + public static boolean fixData(LevelStorageSource.LevelStorageAccess levelStorageAccess, + boolean showUI, + Consumer onResume) { + File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile(); + File levelDat = levelStorageAccess.getLevelPath(LevelResource.LEVEL_DATA_FILE).toFile(); + boolean newWorld = false; + if (!levelDat.exists()) { + BCLib.LOGGER.info("Creating a new World, no fixes needed"); + newWorld = true; + } + + initializeWorldData(levelPath, newWorld); + if (newWorld) return false; + + return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume); + } + + /** + * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the + * current versions of the plugins. + *

+ * This implementation will create a new {@link LevelStorageAccess} and call {@link #initializeWorldData(File, boolean)} + * using the provided root path. + * + * @param levelSource The SourceStorage for this Minecraft instance, You can get this using + * {@code Minecraft.getInstance().getLevelSource()} + * @param levelID The ID of the Level you want to patch + * @param newWorld {@code true} if this is a fresh world + */ + public static void initializeWorldData(LevelStorageSource levelSource, String levelID, boolean newWorld) { + wrapCall(levelSource, levelID, (levelStorageAccess) -> { + initializeWorldData(levelStorageAccess, newWorld); + return true; + }); + } + + /** + * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the + * current versions of the plugins. + * + * @param access levelAccess for the worldd + * @param newWorld {@code true} if this is a fresh world + */ + public static void initializeWorldData(LevelStorageAccess access, boolean newWorld) { + initializeWorldData(access.getLevelPath(LevelResource.ROOT).toFile(), newWorld); + } + + /** + * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the + * current versions of the plugins. + * + * @param levelBaseDir Folder of the world + * @param newWorld {@code true} if this is a fresh world + */ + public static void initializeWorldData(File levelBaseDir, boolean newWorld) { + WorldDataAPI.load(new File(levelBaseDir, "data")); + + if (newWorld) { + getMigrationProfile().markApplied(); + WorldDataAPI.saveFile(BCLib.MOD_ID); + } + } + + @Environment(EnvType.CLIENT) + private static AtomicProgressListener showProgressScreen() { + ProgressScreen ps = new ProgressScreen(Minecraft.getInstance().screen, + Component.translatable("title.bclib.datafixer.progress"), + Component.translatable("message.bclib.datafixer.progress")); + Minecraft.getInstance().setScreen(ps); + return ps; + } + + private static boolean fixData(File dir, String levelID, boolean showUI, Consumer onResume) { + MigrationProfile profile = loadProfileIfNeeded(dir); + + BiConsumer runFixes = (createBackup, applyFixes) -> { + final AtomicProgressListener progress; + if (applyFixes) { + if (showUI) { + progress = showProgressScreen(); + } else { + progress = new AtomicProgressListener() { + private long timeStamp = Util.getMillis(); + private AtomicInteger counter = new AtomicInteger(0); + + @Override + public void incAtomic(int maxProgress) { + int percentage = (100 * counter.incrementAndGet()) / maxProgress; + if (Util.getMillis() - this.timeStamp >= 1000L) { + this.timeStamp = Util.getMillis(); + BCLib.LOGGER.info("Patching... {}%", percentage); + } + } + + @Override + public void resetAtomic() { + counter = new AtomicInteger(0); + } + + public void stop() { + } + + public void progressStage(Component component) { + BCLib.LOGGER.info("Patcher Stage... {}%", component.getString()); + } + }; + } + } else { + progress = null; + } + + Supplier runner = () -> { + if (createBackup) { + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.waitbackup")); + EditWorldScreen.makeBackupAndShowToast(Minecraft.getInstance().getLevelSource(), levelID); + } + + if (applyFixes) { + return runDataFixes(dir, profile, progress); + } + + return new State(); + }; + + if (showUI) { + Thread fixerThread = new Thread(() -> { + 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 { + State state = runner.get(); + if (state.hasError()) { + LOGGER.error("There were Errors while fixing the Level:"); + LOGGER.error(state.getErrorMessage()); + } + } + }; + + //we have some migrations + if (profile != null) { + //display the confirm UI. + if (showUI) { + showBackupWarning(levelID, runFixes); + return true; + } else { + BCLib.LOGGER.warning("Applying Fixes on Level"); + runFixes.accept(false, true); + } + } + 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.applyPatches()) { + LOGGER.info("World Patches are disabled"); + return null; + } + + MigrationProfile profile = getMigrationProfile(); + profile.runPrePatches(levelBaseDir); + + if (!profile.hasAnyFixes()) { + LOGGER.info("Everything up to date"); + return null; + } + + return profile; + } + + @NotNull + private static MigrationProfile getMigrationProfile() { + final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); + MigrationProfile profile = Patch.createMigrationData(patchConfig); + return profile; + } + + @Environment(EnvType.CLIENT) + static void showBackupWarning(String levelID, BiConsumer whenFinished) { + Minecraft.getInstance().setScreen(new ConfirmFixScreen(null, whenFinished::accept)); + } + + private static State runDataFixes(File dir, MigrationProfile profile, AtomicProgressListener progress) { + State state = new State(); + progress.resetAtomic(); + + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.reading")); + List players = getAllPlayers(dir); + List regions = getAllRegions(dir, null); + final int maxProgress = players.size() + regions.size() + 4; + progress.incAtomic(maxProgress); + + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.players")); + players.parallelStream().forEach((file) -> { + fixPlayer(profile, state, file); + progress.incAtomic(maxProgress); + }); + + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.level")); + fixLevel(profile, state, dir); + progress.incAtomic(maxProgress); + + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.worlddata")); + try { + profile.patchWorldData(); + } catch (PatchDidiFailException e) { + state.didFail = true; + state.addError("Failed fixing worldconfig (" + e.getMessage() + ")"); + BCLib.LOGGER.error(e.getMessage()); + } + progress.incAtomic(maxProgress); + + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.regions")); + regions.parallelStream().forEach((file) -> { + fixRegion(profile, state, file); + progress.incAtomic(maxProgress); + }); + + if (!state.didFail) { + progress.progressStage(Component.translatable("message.bclib.datafixer.progress.saving")); + profile.markApplied(); + WorldDataAPI.saveFile(BCLib.MOD_ID); + } + progress.incAtomic(maxProgress); + + progress.stop(); + + return state; + } + + private static void fixLevel(MigrationProfile profile, State state, File levelBaseDir) { + try { + LOGGER.info("Inspecting level.dat in " + levelBaseDir); + + //load the level (could already contain patches applied by patchLevelDat) + CompoundTag level = profile.getLevelDat(levelBaseDir); + boolean[] changed = {profile.isLevelDatChanged()}; + + if (profile.getPrePatchException() != null) { + throw profile.getPrePatchException(); + } + + if (level.contains("Data")) { + CompoundTag dataTag = (CompoundTag) level.get("Data"); + if (dataTag.contains("Player")) { + CompoundTag player = (CompoundTag) dataTag.get("Player"); + fixPlayerNbt(player, changed, profile); + } + } + + if (changed[0]) { + LOGGER.warning("Writing '{}'", profile.getLevelDatFile()); + NbtIo.writeCompressed(level, profile.getLevelDatFile()); + } + } 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 = readNbt(file); + boolean[] changed = {false}; + fixPlayerNbt(player, changed, data); + + if (changed[0]) { + LOGGER.warning("Writing '{}'", file); + NbtIo.writeCompressed(player, file); + } + } 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); + fixItemArrayWithID(inventory, changed, data, true); + + //Checking EnderChest + ListTag enderitems = player.getList("EnderItems", Tag.TAG_COMPOUND); + fixItemArrayWithID(enderitems, changed, data, true); + + //Checking ReceipBook + if (player.contains("recipeBook")) { + CompoundTag recipeBook = player.getCompound("recipeBook"); + changed[0] |= fixStringIDList(recipeBook, "recipes", data); + changed[0] |= fixStringIDList(recipeBook, "toBeDisplayed", data); + } + } + + static boolean fixStringIDList(CompoundTag root, String name, MigrationProfile data) { + boolean _changed = false; + if (root.contains(name)) { + ListTag items = root.getList(name, Tag.TAG_STRING); + ListTag newItems = new ListTag(); + + for (Tag tag : items) { + final StringTag str = (StringTag) tag; + final String replace = data.replaceStringFromIDs(str.getAsString()); + if (replace != null) { + _changed = true; + newItems.add(StringTag.valueOf(replace)); + } else { + newItems.add(tag); + } + } + if (_changed) { + root.put(name, newItems); + } + } + return _changed; + } + + private static void fixRegion(MigrationProfile data, State state, File file) { + try { + Path path = file.toPath(); + LOGGER.info("Inspecting " + path); + boolean[] changed = new boolean[1]; + RegionFile region = new RegionFile(path, path.getParent(), true); + + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + ChunkPos pos = new ChunkPos(x, z); + changed[0] = false; + 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"))) { + // NbtIo.write(root, new File(file.toString() + "-" + x + "-" + z + ".nbt")); + // } + input.close(); + + //Checking TileEntities + ListTag tileEntities = root.getCompound("Level") + .getList("TileEntities", Tag.TAG_COMPOUND); + fixItemArrayWithID(tileEntities, changed, data, true); + + //Checking Entities + ListTag entities = root.getList("Entities", Tag.TAG_COMPOUND); + fixItemArrayWithID(entities, changed, data, true); + + //Checking Block Palette + ListTag sections = root.getCompound("Level") + .getList("Sections", Tag.TAG_COMPOUND); + sections.forEach((tag) -> { + ListTag palette = ((CompoundTag) tag).getList("Palette", Tag.TAG_COMPOUND); + palette.forEach((blockTag) -> { + 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]) { + LOGGER.warning("Writing '{}': {}/{}", file, x, z); + // NbtIo.write(root, new File(file.toString() + "-" + x + "-" + z + "-changed.nbt")); + DataOutputStream output = region.getChunkDataOutputStream(pos); + NbtIo.write(root, output); + output.close(); + } + } + } + } + region.close(); + } catch (Exception e) { + 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) { + patchConfTag = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); + } + return patchConfTag; + } + + static void fixItemArrayWithID(ListTag items, boolean[] changed, MigrationProfile data, boolean recursive) { + items.forEach(inTag -> { + fixID((CompoundTag) inTag, changed, data, recursive); + }); + } + + + static void fixID(CompoundTag inTag, boolean[] changed, MigrationProfile data, boolean recursive) { + final CompoundTag tag = inTag; + + changed[0] |= data.replaceStringFromIDs(tag, "id"); + if (tag.contains("Item")) { + CompoundTag item = (CompoundTag) tag.get("Item"); + fixID(item, changed, data, recursive); + } + + if (recursive && tag.contains("Items")) { + fixItemArrayWithID(tag.getList("Items", Tag.TAG_COMPOUND), changed, data, true); + } + if (recursive && tag.contains("Inventory")) { + ListTag inventory = tag.getList("Inventory", Tag.TAG_COMPOUND); + fixItemArrayWithID(inventory, changed, data, true); + } + if (tag.contains("tag")) { + CompoundTag entityTag = (CompoundTag) tag.get("tag"); + if (entityTag.contains("BlockEntityTag")) { + CompoundTag blockEntityTag = (CompoundTag) entityTag.get("BlockEntityTag"); + fixID(blockEntityTag, changed, data, recursive); + /*ListTag items = blockEntityTag.getList("Items", Tag.TAG_COMPOUND); + fixItemArrayWithID(items, changed, data, recursive);*/ + } + } + } + + private static List getAllPlayers(File dir) { + List list = new ArrayList<>(); + dir = new File(dir, "playerdata"); + if (!dir.exists() || !dir.isDirectory()) { + return list; + } + for (File file : dir.listFiles()) { + if (file.isFile() && file.getName().endsWith(".dat")) { + list.add(file); + } + } + return list; + } + + private static List getAllRegions(File dir, List list) { + if (list == null) { + list = new ArrayList<>(); + } + for (File file : dir.listFiles()) { + if (file.isDirectory()) { + getAllRegions(file, list); + } else if (file.isFile() && file.getName().endsWith(".mca")) { + list.add(file); + } + } + return list; + } + + /** + * register a new Patch + * + * @param patch A #Supplier that will instantiate the new Patch Object + */ + public static void registerPatch(Supplier patch) { + Patch.getALL().add(patch.get()); + } + + private static CompoundTag readNbt(File file) throws IOException { + try { + return NbtIo.readCompressed(file); + } catch (ZipException | EOFException e) { + return NbtIo.read(file); + } + } + +} diff --git a/src/main/java/org/betterx/bclib/api/datafixer/ForcedLevelPatch.java b/src/main/java/org/betterx/bclib/api/datafixer/ForcedLevelPatch.java new file mode 100644 index 00000000..c81b31c0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/datafixer/ForcedLevelPatch.java @@ -0,0 +1,57 @@ +package org.betterx.bclib.api.datafixer; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import org.betterx.bclib.interfaces.PatchBiFunction; +import org.betterx.bclib.interfaces.PatchFunction; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + + +/** + * 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 getIDReplacements() { + return new HashMap(); + } + + @Override + public final PatchFunction getWorldDataPatcher() { + return null; + } + + @Override + public final PatchBiFunction getBlockStatePatcher() { + return null; + } + + @Override + public final List getWorldDataIDPaths() { + return null; + } + + @Override + public PatchFunction 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); +} + diff --git a/src/main/java/org/betterx/bclib/api/datafixer/MigrationProfile.java b/src/main/java/org/betterx/bclib/api/datafixer/MigrationProfile.java new file mode 100644 index 00000000..ee548a69 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/datafixer/MigrationProfile.java @@ -0,0 +1,372 @@ +package org.betterx.bclib.api.datafixer; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.Tag; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.WorldDataAPI; +import org.betterx.bclib.interfaces.PatchBiFunction; +import org.betterx.bclib.interfaces.PatchFunction; +import org.betterx.bclib.util.ModUtil; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; + +public class MigrationProfile { + final Set mods; + final Map idReplacements; + final List> levelPatchers; + final List> statePatchers; + final List worldDataPatchers; + final Map> worldDataIDPaths; + + private final CompoundTag config; + private CompoundTag level; + private File levelBaseDir; + private boolean prePatchChangedLevelDat; + private boolean didRunPrePatch; + private Exception prePatchException; + + MigrationProfile(CompoundTag config, boolean applyAll) { + this.config = config; + + this.mods = Collections.unmodifiableSet(Patch.getALL() + .stream() + .map(p -> p.modID) + .collect(Collectors.toSet())); + + HashMap replacements = new HashMap(); + List> levelPatches = new LinkedList<>(); + List worldDataPatches = new LinkedList<>(); + List> statePatches = new LinkedList<>(); + HashMap> worldDataIDPaths = new HashMap<>(); + for (String modID : mods) { + + Patch.getALL() + .stream() + .filter(p -> p.modID.equals(modID)) + .forEach(patch -> { + List paths = patch.getWorldDataIDPaths(); + if (paths != null) worldDataIDPaths.put(modID, paths); + + 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 { + DataFixerAPI.LOGGER.info("Ignoring " + patch); + } + }); + } + + this.worldDataIDPaths = Collections.unmodifiableMap(worldDataIDPaths); + 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 nbts = getAllNbts(dir, null); + nbts.parallelStream().forEach((file) -> { + DataFixerAPI.LOGGER.info("Loading NBT " + file); + try { + CompoundTag root = NbtIo.readCompressed(file); + boolean[] changed = {false}; + int spawnerIdx = -1; + if (root.contains("palette")) { + ListTag items = root.getList("palette", Tag.TAG_COMPOUND); + for (int idx = 0; idx < items.size(); idx++) { + final CompoundTag tag = (CompoundTag) items.get(idx); + if (tag.contains("Name") && tag.getString("Name").equals("minecraft:spawner")) + spawnerIdx = idx; + if (tag.contains("Name") && (tag.getString("Name").equals("minecraft:") || tag.getString("Name") + .equals(""))) { + System.out.println("Empty Name"); + } + if (tag.contains("id") && (tag.getString("id").equals("minecraft:") || tag.getString("id") + .equals(""))) { + System.out.println("Empty ID"); + } + changed[0] |= profile.replaceStringFromIDs(tag, "Name"); + } + } + + if (spawnerIdx >= 0 && root.contains("blocks")) { + ListTag items = root.getList("blocks", Tag.TAG_COMPOUND); + for (int idx = 0; idx < items.size(); idx++) { + final CompoundTag blockTag = (CompoundTag) items.get(idx); + if (blockTag.contains("state") && blockTag.getInt("state") == spawnerIdx && blockTag.contains( + "nbt")) { + CompoundTag nbt = blockTag.getCompound("nbt"); + if (nbt.contains("SpawnData")) { + final CompoundTag entity = nbt.getCompound("SpawnData"); + if (!entity.contains("entity")) { + CompoundTag data = new CompoundTag(); + data.put("entity", entity); + nbt.put("SpawnData", data); + + changed[0] = true; + } + } + if (nbt.contains("SpawnPotentials")) { + ListTag pots = nbt.getList("SpawnPotentials", Tag.TAG_COMPOUND); + for (Tag potItemIn : pots) { + final CompoundTag potItem = (CompoundTag) potItemIn; + if (potItem.contains("Weight")) { + int weight = potItem.getInt("Weight"); + potItem.putInt("weight", weight); + potItem.remove("Weight"); + + changed[0] = true; + } + + if (potItem.contains("Entity")) { + CompoundTag entity = potItem.getCompound("Entity"); + CompoundTag data = new CompoundTag(); + data.put("entity", entity); + + potItem.put("data", data); + potItem.remove("Entity"); + + changed[0] = true; + } + } + } + } + } + } + + if (changed[0]) { + DataFixerAPI.LOGGER.info("Writing NBT " + file); + NbtIo.writeCompressed(root, file); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + private static List getAllNbts(File dir, List 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) { + if (level == null || this.levelBaseDir == null || !this.levelBaseDir.equals(levelBaseDir)) { + runPrePatches(levelBaseDir); + } + return level; + } + + final public boolean isLevelDatChanged() { + return prePatchChangedLevelDat; + } + + final public File getLevelDatFile() { + return new File(levelBaseDir, "level.dat"); + } + + final public Exception getPrePatchException() { + return prePatchException; + } + + + final public void runPrePatches(File levelBaseDir) { + if (didRunPrePatch) { + BCLib.LOGGER.warning("Already did run PrePatches for " + this.levelBaseDir + "."); + } + BCLib.LOGGER.info("Running Pre Patchers on " + levelBaseDir); + + this.levelBaseDir = levelBaseDir; + this.level = null; + this.prePatchException = null; + didRunPrePatch = true; + + this.prePatchChangedLevelDat = runPreLevelPatches(getLevelDatFile()); + } + + private boolean runPreLevelPatches(File levelDat) { + try { + level = NbtIo.readCompressed(levelDat); + + boolean changed = patchLevelDat(level); + return changed; + } catch (IOException | PatchDidiFailException e) { + prePatchException = e; + return false; + } + } + + 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))); + if (config != null) + config.putString(modID, Patch.maxPatchVersion(modID)); + } + } + + public String currentPatchVersion(@NotNull String modID) { + if (config == null || !config.contains(modID)) return "0.0.0"; + return config.getString(modID); + } + + public int currentPatchLevel(@NotNull String modID) { + return ModUtil.convertModVersion(currentPatchVersion(modID)); + } + + public boolean hasAnyFixes() { + boolean hasLevelDatPatches; + if (didRunPrePatch != false) { + hasLevelDatPatches = prePatchChangedLevelDat; + } else { + hasLevelDatPatches = levelPatchers.size() > 0; + } + + return idReplacements.size() > 0 || hasLevelDatPatches || worldDataPatchers.size() > 0; + } + + public String replaceStringFromIDs(@NotNull String val) { + final String replace = idReplacements.get(val); + return replace; + } + + public boolean replaceStringFromIDs(@NotNull CompoundTag tag, @NotNull String key) { + if (!tag.contains(key)) return false; + + final String val = tag.getString(key); + final String replace = idReplacements.get(val); + + if (replace != null) { + DataFixerAPI.LOGGER.warning("Replacing ID '{}' with '{}'.", val, replace); + tag.putString(key, replace); + return true; + } + + return false; + } + + private boolean replaceIDatPath(@NotNull ListTag list, @NotNull String[] parts, int level) { + boolean[] changed = {false}; + if (level == parts.length - 1) { + DataFixerAPI.fixItemArrayWithID(list, changed, this, true); + } else { + list.forEach(inTag -> changed[0] |= replaceIDatPath((CompoundTag) inTag, parts, level + 1)); + } + return changed[0]; + } + + private boolean replaceIDatPath(@NotNull CompoundTag tag, @NotNull String[] parts, int level) { + boolean changed = false; + for (int i = level; i < parts.length - 1; i++) { + final String part = parts[i]; + if (tag.contains(part)) { + final byte type = tag.getTagType(part); + if (type == Tag.TAG_LIST) { + ListTag list = tag.getList(part, Tag.TAG_COMPOUND); + return replaceIDatPath(list, parts, i); + } else if (type == Tag.TAG_COMPOUND) { + tag = tag.getCompound(part); + } + } else { + return false; + } + } + + if (tag != null && parts.length > 0) { + final String key = parts[parts.length - 1]; + final byte type = tag.getTagType(key); + if (type == Tag.TAG_LIST) { + final ListTag list = tag.getList(key, Tag.TAG_COMPOUND); + final boolean[] _changed = {false}; + if (list.size() == 0) { + _changed[0] = DataFixerAPI.fixStringIDList(tag, key, this); + } else { + DataFixerAPI.fixItemArrayWithID(list, _changed, this, true); + } + return _changed[0]; + } else if (type == Tag.TAG_STRING) { + return replaceStringFromIDs(tag, key); + } else if (type == Tag.TAG_COMPOUND) { + final CompoundTag cTag = tag.getCompound(key); + boolean[] _changed = {false}; + DataFixerAPI.fixID(cTag, _changed, this, true); + return _changed[0]; + } + } + + + return false; + } + + public boolean replaceIDatPath(@NotNull CompoundTag root, @NotNull String path) { + String[] parts = path.split("\\."); + return replaceIDatPath(root, parts, 0); + } + + public boolean patchLevelDat(@NotNull CompoundTag level) throws PatchDidiFailException { + boolean changed = false; + for (PatchFunction f : levelPatchers) { + changed |= f.apply(level, this); + } + return changed; + } + + public void patchWorldData() throws PatchDidiFailException { + for (Patch patch : worldDataPatchers) { + CompoundTag root = WorldDataAPI.getRootTag(patch.modID); + boolean changed = patch.getWorldDataPatcher().apply(root, this); + if (changed) { + WorldDataAPI.saveFile(patch.modID); + } + } + + for (Map.Entry> entry : worldDataIDPaths.entrySet()) { + CompoundTag root = WorldDataAPI.getRootTag(entry.getKey()); + boolean[] changed = {false}; + entry.getValue().forEach(path -> { + changed[0] |= replaceIDatPath(root, path); + }); + + if (changed[0]) { + WorldDataAPI.saveFile(entry.getKey()); + } + } + } + + public boolean patchBlockState(ListTag palette, ListTag states) throws PatchDidiFailException { + boolean changed = false; + for (PatchBiFunction f : statePatchers) { + changed |= f.apply(palette, states, this); + } + return changed; + } +} diff --git a/src/main/java/org/betterx/bclib/api/datafixer/Patch.java b/src/main/java/org/betterx/bclib/api/datafixer/Patch.java new file mode 100644 index 00000000..a06aebaa --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/datafixer/Patch.java @@ -0,0 +1,238 @@ +package org.betterx.bclib.api.datafixer; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import org.betterx.bclib.api.WorldDataAPI; +import org.betterx.bclib.interfaces.PatchBiFunction; +import org.betterx.bclib.interfaces.PatchFunction; +import org.betterx.bclib.util.ModUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +public abstract class Patch { + private static final List ALL = new ArrayList<>(10); + + /** + * The Patch-Level derived from {@link #version} + */ + public final int level; + + /** + * The Patch-Version string + */ + public final String version; + + /** + * The Mod-ID that registered this Patch + */ + + @NotNull + public final String modID; + + /** + * This Mod is tested for each level start + */ + public final boolean alwaysApply; + + static List getALL() { + return ALL; + } + + /** + * Returns the highest Patch-Version that is available for the given mod. If no patches were + * registerd for the mod, this will return 0.0.0 + * + * @param modID The ID of the mod you want to query + * @return The highest Patch-Version that was found + */ + public static String maxPatchVersion(@NotNull String modID) { + return ALL.stream().filter(p -> p.modID.equals(modID)).map(p -> p.version).reduce((p, c) -> c).orElse("0.0.0"); + } + + /** + * Returns the highest patch-level that is available for the given mod. If no patches were + * registerd for the mod, this will return 0 + * + * @param modID The ID of the mod you want to query + * @return The highest Patch-Level that was found + */ + public static int maxPatchLevel(@NotNull String modID) { + return ALL.stream().filter(p -> p.modID.equals(modID)).mapToInt(p -> p.level).max().orElse(0); + } + + /** + * Called by inheriting classes. + *

+ * Performs some sanity checks on the values and might throw a #RuntimeException if any + * inconsistencies are found. + * + * @param modID The ID of the Mod you want to register a patch for. This should be your + * ModID only. The ModID can not be {@code null} or an empty String. + * @param version The mod-version that introduces the patch. This needs Semantic-Version String + * like x.x.x. Developers are responsible for registering their patches in the correct + * order (with increasing versions). You are not allowed to register a new + * Patch with a version lower or equal than + * {@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 || modID.isEmpty()) { + throw new RuntimeException("[INTERNAL ERROR] Patches need a valid modID!"); + } + + if (version == null || version.isEmpty()) { + throw new RuntimeException("Invalid Mod-Version"); + } + + this.version = version; + this.alwaysApply = alwaysApply; + this.level = ModUtil.convertModVersion(version); + if (!ALL.stream().filter(p -> p.modID.equals(modID)).noneMatch(p -> p.level >= this.level) || this.level <= 0) { + throw new RuntimeException( + "[INTERNAL ERROR] Patch-levels need to be created in ascending order beginning with 1."); + } + + this.modID = modID; + } + + @Override + public String toString() { + return "Patch{" + modID + ':' + version + ':' + level + '}'; + } + + + /** + * Return block data fixes. Fixes will be applied on world load if current patch-level for + * the linked mod is lower than the {@link #level}. + *

+ * The default implementation of this method returns an empty map. + * + * @return The returned Map should contain the replacements. All occurences of the + * {@code KeySet} are replaced with the associated value. + */ + public Map getIDReplacements() { + return new HashMap(); + } + + /** + * Return a {@link PatchFunction} that is called with the content of level.dat. + *

+ * 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 PatchFunction getLevelDatPatcher() { + return null; + } + + /** + * Return a {@link PatchFunction} that is called with the content from the + * {@link WorldDataAPI} for this Mod. + * 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 PatchFunction 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 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. + *

+ * 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* + * @return a new {@link MigrationProfile} Object. + */ + static MigrationProfile createMigrationData(CompoundTag 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 stores IDs in your {@link WorldDataAPI}-File. + *

+ * {@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()} + *

+ * The end of the path can either be a {@link net.minecraft.nbt.StringTag}, a {@link net.minecraft.nbt.ListTag} or + * a {@link CompoundTag}. If the Path contains a non-leaf {@link net.minecraft.nbt.ListTag}, all members of that + * list will be processed. For example: + *

+     * 	 - global +
+     * 			  | - key (String)
+     * 			  | - items (List) +
+     * 							   | - { id (String) }
+     * 							   | - { id (String) }
+     * 
+ * The path global.items.id will fix all id-entries in the items-list, while the path + * global.key will only fix the key-entry. + *

+ * if the leaf-entry (= the last part of the path, which would be items in global.items) is a + * {@link CompoundTag}, the system will fix any id entry. If the {@link CompoundTag} contains an item + * or tag.BlockEntityTag entry, the system will recursivley continue with those. If an items + * or inventory-{@link net.minecraft.nbt.ListTag} was found, the system will continue recursivley with + * every item of that list. + *

+ * if the leaf-entry is a {@link net.minecraft.nbt.ListTag}, it is handle the same as a child items entry + * of a {@link CompoundTag}. + * + * @return {@code null} if nothing changes or a list of Paths in your {@link WorldDataAPI}-File. + * Paths are dot-seperated (see {@link WorldDataAPI#getCompoundTag(String, String)}). + */ + public List getWorldDataIDPaths() { + return null; + } +} diff --git a/src/main/java/org/betterx/bclib/api/datafixer/PatchDidiFailException.java b/src/main/java/org/betterx/bclib/api/datafixer/PatchDidiFailException.java new file mode 100644 index 00000000..44e77f9a --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/datafixer/PatchDidiFailException.java @@ -0,0 +1,11 @@ +package org.betterx.bclib.api.datafixer; + +public class PatchDidiFailException extends Exception { + public PatchDidiFailException() { + super(); + } + + public PatchDidiFailException(Exception e) { + super(e); + } +} diff --git a/src/main/java/org/betterx/bclib/api/features/BCLCommonFeatures.java b/src/main/java/org/betterx/bclib/api/features/BCLCommonFeatures.java new file mode 100644 index 00000000..7e901bf9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/features/BCLCommonFeatures.java @@ -0,0 +1,150 @@ +package org.betterx.bclib.api.features; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration; +import net.minecraft.world.level.levelgen.placement.PlacementModifier; +import net.minecraft.world.level.levelgen.structure.templatesystem.BlockMatchTest; + +import org.betterx.bclib.world.features.BCLFeature; + +public class BCLCommonFeatures { + /** + * Will create a basic plant feature. + * + * @param id {@link ResourceLocation} feature ID. + * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. + * @param density iterations per chunk. + * @return new BCLFeature instance. + */ + public static BCLFeature makeVegetationFeature(ResourceLocation id, + Feature feature, + int density) { + return makeVegetationFeature(id, feature, density, false); + } + + /** + * Will create a basic plant feature. + * + * @param id {@link ResourceLocation} feature ID. + * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. + * @param density iterations per chunk. + * @param allHeight if {@code true} will generate plant on all layers, if {@code false} - only on surface. + * @return new BCLFeature instance. + */ + public static BCLFeature makeVegetationFeature(ResourceLocation id, + Feature feature, + int density, + boolean allHeight) { + if (allHeight) { + return BCLFeatureBuilder.start(id, feature).countLayers(density).squarePlacement().onlyInBiome().build(); + } else { + return BCLFeatureBuilder + .start(id, feature) + .countMax(density) + .squarePlacement() + .heightmap() + .onlyInBiome() + .build(); + } + } + + /** + * Will create feature which will be generated once in each chunk. + * + * @param id {@link ResourceLocation} feature ID. + * @param decoration {@link Decoration} feature step. + * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. + * @return new BCLFeature instance. + */ + public static BCLFeature makeChunkFeature(ResourceLocation id, + Decoration decoration, + Feature feature) { + return BCLFeatureBuilder.start(id, feature).decoration(decoration).count(1).onlyInBiome().build(); + } + + /** + * Will create feature with chanced decoration, chance for feature to generate per chunk is 1 / chance. + * + * @param id {@link ResourceLocation} feature ID. + * @param decoration {@link Decoration} feature step. + * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. + * @param chance chance for feature to be generated in. + * @return new BCLFeature instance. + */ + public static BCLFeature makeChancedFeature(ResourceLocation id, + Decoration decoration, + Feature feature, + int chance) { + return BCLFeatureBuilder.start(id, feature) + .decoration(decoration) + .oncePerChunks(chance) + .squarePlacement() + .onlyInBiome() + .build(); + } + + /** + * Will create feature with specified generation iterations per chunk. + * + * @param id {@link ResourceLocation} feature ID. + * @param decoration {@link Decoration} feature step. + * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. + * @param count iterations steps. + * @return new BCLFeature instance. + */ + public static BCLFeature makeCountFeature(ResourceLocation id, + Decoration decoration, + Feature feature, + int count) { + return BCLFeatureBuilder.start(id, feature) + .decoration(decoration) + .count(count) + .squarePlacement() + .onlyInBiome() + .build(); + } + + /** + * Will create a basic ore feature. + * + * @param id {@link ResourceLocation} feature ID. + * @param blockOre {@link Decoration} feature step. + * @param hostBlock {@link Block} to generate feature in. + * @param veins iterations per chunk. + * @param veinSize size of ore vein. + * @param airDiscardChance chance that this orge gets discarded when it is exposed to air + * @param placement {@link net.minecraft.world.level.levelgen.placement.PlacementModifier} for the ore distribution, + * for example {@code PlacementUtils.FULL_RANGE}, {@code PlacementUtils.RANGE_10_10} + * @param rare when true, this is placed as a rare resource + * @return new BCLFeature instance. + */ + public static BCLFeature makeOreFeature(ResourceLocation id, + Block blockOre, + Block hostBlock, + int veins, + int veinSize, + float airDiscardChance, + PlacementModifier placement, + boolean rare) { + BCLFeatureBuilder builder = BCLFeatureBuilder.start(id, Feature.ORE).decoration(Decoration.UNDERGROUND_ORES); + + if (rare) { + builder.oncePerChunks(veins); + } else { + builder.count(veins); + } + + builder.modifier(placement).squarePlacement().onlyInBiome(); + + return builder.build(new OreConfiguration( + new BlockMatchTest(hostBlock), + blockOre.defaultBlockState(), + veinSize, + airDiscardChance + )); + } +} diff --git a/src/main/java/org/betterx/bclib/api/features/BCLFeatureBuilder.java b/src/main/java/org/betterx/bclib/api/features/BCLFeatureBuilder.java new file mode 100644 index 00000000..d03c97a1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/features/BCLFeatureBuilder.java @@ -0,0 +1,162 @@ +package org.betterx.bclib.api.features; + +import net.minecraft.data.worldgen.placement.PlacementUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.placement.*; + +import org.betterx.bclib.world.features.BCLFeature; + +import java.util.ArrayList; +import java.util.List; + +public class BCLFeatureBuilder> { + private static final BCLFeatureBuilder INSTANCE = new BCLFeatureBuilder(); + private final List modifications = new ArrayList<>(16); + private ResourceLocation featureID; + private Decoration decoration; + private F feature; + + private BCLFeatureBuilder() { + } + + /** + * Starts a new {@link BCLFeature} builder. + * + * @param featureID {@link ResourceLocation} feature identifier. + * @param feature {@link Feature} to construct. + * @return {@link BCLFeatureBuilder} instance. + */ + public static BCLFeatureBuilder start(ResourceLocation featureID, Feature feature) { + INSTANCE.decoration = Decoration.VEGETAL_DECORATION; + INSTANCE.modifications.clear(); + INSTANCE.featureID = featureID; + INSTANCE.feature = feature; + return INSTANCE; + } + + /** + * Set generation step for the feature. Default is {@code VEGETAL_DECORATION}. + * + * @param decoration {@link Decoration} step. + * @return same {@link BCLFeatureBuilder} instance. + */ + public BCLFeatureBuilder decoration(Decoration decoration) { + this.decoration = decoration; + return this; + } + + /** + * Add feature placement modifier. Used as a condition for feature how to generate. + * + * @param modifier {@link PlacementModifier}. + * @return same {@link BCLFeatureBuilder} instance. + */ + public BCLFeatureBuilder modifier(PlacementModifier modifier) { + modifications.add(modifier); + return this; + } + + public BCLFeatureBuilder modifier(List modifiers) { + modifications.addAll(modifiers); + return this; + } + + /** + * Generate feature in certain iterations (per chunk). + * + * @param count how many times feature will be generated in chunk. + * @return same {@link BCLFeatureBuilder} instance. + */ + public BCLFeatureBuilder count(int count) { + return modifier(CountPlacement.of(count)); + } + + /** + * Generate feature in certain iterations (per chunk). Count can be between 0 and max value. + * + * @param count maximum amount of iterations per chunk. + * @return same {@link BCLFeatureBuilder} instance. + */ + public BCLFeatureBuilder countMax(int count) { + return modifier(CountPlacement.of(UniformInt.of(0, count))); + } + + /** + * Generate feature in certain iterations (per chunk). + * Feature will be generated on all layers (example - Nether plants). + * + * @param count how many times feature will be generated in chunk layers. + * @return same {@link BCLFeatureBuilder} instance. + */ + @SuppressWarnings("deprecation") + public BCLFeatureBuilder countLayers(int count) { + return modifier(CountOnEveryLayerPlacement.of(count)); + } + + /** + * Generate feature in certain iterations (per chunk). Count can be between 0 and max value. + * Feature will be generated on all layers (example - Nether plants). + * + * @param count maximum amount of iterations per chunk layers. + * @return same {@link BCLFeatureBuilder} instance. + */ + @SuppressWarnings("deprecation") + public BCLFeatureBuilder countLayersMax(int count) { + return modifier(CountOnEveryLayerPlacement.of(UniformInt.of(0, count))); + } + + /** + * Will place feature once in certain amount of chunks (in average). + * + * @param chunks amount of chunks. + * @return same {@link BCLFeatureBuilder} instance. + */ + public BCLFeatureBuilder oncePerChunks(int chunks) { + return modifier(RarityFilter.onAverageOnceEvery(chunks)); + } + + /** + * Restricts feature generation only to biome where feature was added. + * + * @return same {@link BCLFeatureBuilder} instance. + */ + public BCLFeatureBuilder onlyInBiome() { + return modifier(BiomeFilter.biome()); + } + + // Are these two things required in 1.18.1? + // TODO - add information + public BCLFeatureBuilder squarePlacement() { + return modifier(InSquarePlacement.spread()); + } + + // TODO - add information + public BCLFeatureBuilder heightmap() { + return modifier(PlacementUtils.HEIGHTMAP); + } + + /** + * Builds a new {@link BCLFeature} instance. Features will be registered during this process. + * + * @param configuration any {@link FeatureConfiguration} for provided {@link Feature}. + * @return created {@link BCLFeature} instance. + */ + public BCLFeature build(FC configuration) { + PlacementModifier[] modifiers = modifications.toArray(new PlacementModifier[modifications.size()]); + return new BCLFeature(featureID, feature, decoration, configuration, modifiers); + } + + /** + * Builds a new {@link BCLFeature} instance with {@code NONE} {@link FeatureConfiguration}. + * Features will be registered during this process. + * + * @return created {@link BCLFeature} instance. + */ + public BCLFeature build() { + return build((FC) FeatureConfiguration.NONE); + } +} diff --git a/src/main/java/org/betterx/bclib/api/spawning/SpawnRuleBuilder.java b/src/main/java/org/betterx/bclib/api/spawning/SpawnRuleBuilder.java new file mode 100644 index 00000000..83ef28c1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/spawning/SpawnRuleBuilder.java @@ -0,0 +1,361 @@ +package org.betterx.bclib.api.spawning; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.Difficulty; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.SpawnPlacements.SpawnPredicate; +import net.minecraft.world.entity.SpawnPlacements.Type; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.phys.AABB; + +import net.fabricmc.fabric.mixin.object.builder.SpawnRestrictionAccessor; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.entity.BCLEntityWrapper; +import org.betterx.bclib.interfaces.SpawnRule; + +import java.util.*; +import java.util.function.Supplier; + +public class SpawnRuleBuilder { + private static final Map RULES_CACHE = Maps.newHashMap(); + private static final SpawnRuleBuilder INSTANCE = new SpawnRuleBuilder(); + private final List rules = Lists.newArrayList(); + private SpawnRuleEntry entryInstance; + private EntityType entityType; + + private SpawnRuleBuilder() { + } + + /** + * Starts new rule building process. + * + * @param entityType The entity you want to build a rule for + * @return prepared {@link SpawnRuleBuilder} instance. + */ + public static SpawnRuleBuilder start(EntityType entityType) { + INSTANCE.entityType = entityType; + INSTANCE.rules.clear(); + return INSTANCE; + } + + /** + * Starts new rule building process. + * + * @param wrapper The entity you want to build a rule for + * @return prepared {@link SpawnRuleBuilder} instance. + */ + public static SpawnRuleBuilder start(BCLEntityWrapper wrapper) { + SpawnRuleBuilder builder = start(wrapper.type()); + if (!wrapper.canSpawn()) { + builder.preventSpawn(); + } + return builder; + } + + /** + * Stop entity spawn entierly + * + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder preventSpawn() { + entryInstance = getFromCache("prevent", () -> { + return new SpawnRuleEntry(-1, (type, world, spawnReason, pos, random) -> false); + }); + rules.add(entryInstance); + return this; + } + + /** + * Stop entity spawn on peaceful {@link Difficulty} + * + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder notPeaceful() { + entryInstance = getFromCache("not_peaceful", () -> { + return new SpawnRuleEntry(0, + (type, world, spawnReason, pos, random) -> world.getDifficulty() != Difficulty.PEACEFUL); + }); + rules.add(entryInstance); + return this; + } + + /** + * Restricts entity spawn above world surface (flying mobs). + * + * @param minHeight minimal spawn height. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder aboveGround(int minHeight) { + entryInstance = getFromCache("above_ground", () -> { + return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> { + if (pos.getY() < world.getMinBuildHeight() + 2) { + return false; + } + return pos.getY() > world.getHeight(Types.WORLD_SURFACE, pos.getX(), pos.getZ()) + minHeight; + }); + }); + rules.add(entryInstance); + return this; + } + + /** + * Restricts entity spawn below world logical height (useful for Nether mobs). + * + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder belowMaxHeight() { + entryInstance = getFromCache("below_max_height", () -> { + return new SpawnRuleEntry(0, + (type, world, spawnReason, pos, random) -> pos.getY() < world.dimensionType() + .logicalHeight()); + }); + rules.add(entryInstance); + return this; + } + + /** + * Restricts spawning only to vanilla valid blocks. + * + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder onlyOnValidBlocks() { + entryInstance = getFromCache("only_on_valid_blocks", () -> { + return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> { + BlockPos below = pos.below(); + return world.getBlockState(below).isValidSpawn(world, below, type); + }); + }); + rules.add(entryInstance); + return this; + } + + /** + * Restricts spawning only to specified blocks. + * + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder onlyOnBlocks(Block... blocks) { + final Block[] floorBlocks = blocks; + Arrays.sort(floorBlocks, Comparator.comparing(Block::getDescriptionId)); + + StringBuilder builder = new StringBuilder("only_on_blocks"); + for (Block block : floorBlocks) { + builder.append('_'); + builder.append(block.getDescriptionId()); + } + + entryInstance = getFromCache(builder.toString(), () -> { + return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> { + Block below = world.getBlockState(pos.below()).getBlock(); + for (Block floor : floorBlocks) { + if (floor == below) { + return true; + } + } + return false; + }); + }); + + rules.add(entryInstance); + return this; + } + + /** + * Will spawn entity with 1 / chance probability (randomly). + * + * @param chance probability limit. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder withChance(int chance) { + entryInstance = getFromCache("with_chance_" + chance, () -> { + return new SpawnRuleEntry(1, (type, world, spawnReason, pos, random) -> random.nextInt(chance) == 0); + }); + rules.add(entryInstance); + return this; + } + + /** + * Will spawn entity only below specified brightness value. + * + * @param lightLevel light level upper limit. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder belowBrightness(int lightLevel) { + entryInstance = getFromCache("below_brightness_" + lightLevel, () -> { + return new SpawnRuleEntry(2, + (type, world, spawnReason, pos, random) -> world.getMaxLocalRawBrightness(pos) <= lightLevel); + }); + rules.add(entryInstance); + return this; + } + + /** + * Will spawn entity only above specified brightness value. + * + * @param lightLevel light level lower limit. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder aboveBrightness(int lightLevel) { + entryInstance = getFromCache("above_brightness_" + lightLevel, () -> { + return new SpawnRuleEntry(2, + (type, world, spawnReason, pos, random) -> world.getMaxLocalRawBrightness(pos) >= lightLevel); + }); + rules.add(entryInstance); + return this; + } + + /** + * Entity spawn will follow common vanilla spawn rules - spawn in darkness and not on peaceful level. + * + * @param lightLevel light level upper limit. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder hostile(int lightLevel) { + return notPeaceful().belowBrightness(lightLevel); + } + + /** + * Entity spawn will follow common vanilla spawn rules - spawn in darkness (below light level 7) and not on peaceful level. + * + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder vanillaHostile() { + return hostile(7); + } + + /** + * Will spawn entity only if count of nearby entities will be lower than specified. + * + * @param selectorType selector {@link EntityType} to search. + * @param count max entity count. + * @param side side of box to search in. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder maxNearby(EntityType selectorType, int count, int side) { + final Class baseClass = selectorType.getBaseClass(); + entryInstance = getFromCache("max_nearby_" + selectorType.getDescriptionId() + "_" + count + "_" + side, () -> { + return new SpawnRuleEntry(3, (type, world, spawnReason, pos, random) -> { + try { + final AABB box = new AABB(pos).inflate(side, world.getHeight(), side); + final List list = world.getEntitiesOfClass(baseClass, box, (entity) -> true); + return list.size() < count; + } catch (Exception e) { + return true; + } + }); + }); + rules.add(entryInstance); + return this; + } + + /** + * Will spawn entity only if count of nearby entities with same type will be lower than specified. + * + * @param count max entity count. + * @param side side of box to search in. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder maxNearby(int count, int side) { + return maxNearby(entityType, count, side); + } + + /** + * Will spawn entity only if count of nearby entities with same type will be lower than specified. + * + * @param count max entity count. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder maxNearby(int count) { + return maxNearby(entityType, count, 256); + } + + /** + * Allows to add custom spawning rule for specific entities. + * + * @param rule {@link SpawnRule} rule, can be a lambda expression. + * @return same {@link SpawnRuleBuilder} instance. + */ + public SpawnRuleBuilder customRule(SpawnRule rule) { + rules.add(new SpawnRuleEntry(7, rule)); + return this; + } + + /** + * Finalize spawning rule creation. + * + * @param spawnType {@link Type} of spawn. + * @param heightmapType {@link Types} heightmap type. + */ + public void build(Type spawnType, Types heightmapType) { + final List rulesCopy = Lists.newArrayList(this.rules); + Collections.sort(rulesCopy); + + SpawnPredicate predicate = (entityType, serverLevelAccessor, mobSpawnType, blockPos, random) -> { + for (SpawnRuleEntry rule : rulesCopy) { + if (!rule.canSpawn(entityType, serverLevelAccessor, mobSpawnType, blockPos, random)) { + return false; + } + } + return true; + }; + + SpawnRestrictionAccessor.callRegister(entityType, spawnType, heightmapType, predicate); + } + + /** + * Finalize spawning rule creation with No Restrictions spawn type, useful for flying entities. + * + * @param heightmapType {@link Types} heightmap type. + */ + public void buildNoRestrictions(Types heightmapType) { + build(Type.NO_RESTRICTIONS, heightmapType); + } + + /** + * Finalize spawning rule creation with On Ground spawn type, useful for common entities. + * + * @param heightmapType {@link Types} heightmap type. + */ + public void buildOnGround(Types heightmapType) { + build(Type.ON_GROUND, heightmapType); + } + + /** + * Finalize spawning rule creation with In Water spawn type, useful for water entities. + * + * @param heightmapType {@link Types} heightmap type. + */ + public void buildInWater(Types heightmapType) { + build(Type.IN_WATER, heightmapType); + } + + /** + * Finalize spawning rule creation with In Lava spawn type, useful for lava entities. + * + * @param heightmapType {@link Types} heightmap type. + */ + public void buildInLava(Types heightmapType) { + build(Type.IN_LAVA, heightmapType); + } + + /** + * Internal function, will take entry from cache or create it if necessary. + * + * @param name {@link String} entry internal name. + * @param supplier {@link Supplier} for {@link SpawnRuleEntry}. + * @return new or existing {@link SpawnRuleEntry}. + */ + private static SpawnRuleEntry getFromCache(String name, Supplier supplier) { + SpawnRuleEntry entry = RULES_CACHE.get(name); + if (entry == null) { + entry = supplier.get(); + RULES_CACHE.put(name, entry); + } + return entry; + } +} diff --git a/src/main/java/org/betterx/bclib/api/spawning/SpawnRuleEntry.java b/src/main/java/org/betterx/bclib/api/spawning/SpawnRuleEntry.java new file mode 100644 index 00000000..5d428d20 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/spawning/SpawnRuleEntry.java @@ -0,0 +1,35 @@ +package org.betterx.bclib.api.spawning; + +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.level.LevelAccessor; + +import org.betterx.bclib.interfaces.SpawnRule; + +import org.jetbrains.annotations.NotNull; + +public class SpawnRuleEntry implements Comparable { + private final SpawnRule rule; + private final byte priority; + + public SpawnRuleEntry(int priority, SpawnRule rule) { + this.priority = (byte) priority; + this.rule = rule; + } + + protected boolean canSpawn(EntityType type, + LevelAccessor world, + MobSpawnType spawnReason, + BlockPos pos, + RandomSource random) { + return rule.canSpawn(type, world, spawnReason, pos, random); + } + + @Override + public int compareTo(@NotNull SpawnRuleEntry entry) { + return Integer.compare(priority, entry.priority); + } +} diff --git a/src/main/java/org/betterx/bclib/api/surface/SurfaceRuleBuilder.java b/src/main/java/org/betterx/bclib/api/surface/SurfaceRuleBuilder.java new file mode 100644 index 00000000..e51d97a8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/SurfaceRuleBuilder.java @@ -0,0 +1,301 @@ +package org.betterx.bclib.api.surface; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; +import net.minecraft.world.level.levelgen.placement.CaveSurface; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.api.surface.rules.NoiseCondition; +import org.betterx.bclib.world.surface.DoubleBlockSurfaceNoiseCondition; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class SurfaceRuleBuilder { + private static final Map RULES_CACHE = Maps.newHashMap(); + private static final SurfaceRuleBuilder INSTANCE = new SurfaceRuleBuilder(); + private final List rules = Lists.newArrayList(); + private SurfaceRuleEntry entryInstance; + private ResourceKey biomeKey; + + private SurfaceRuleBuilder() { + } + + public static SurfaceRuleBuilder start() { + INSTANCE.biomeKey = null; + INSTANCE.rules.clear(); + return INSTANCE; + } + + /** + * Restricts surface to only one biome. + * + * @param biomeKey {@link ResourceKey} for the {@link Biome}. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder biome(ResourceKey biomeKey) { + this.biomeKey = biomeKey; + return this; + } + + /** + * Restricts surface to only one biome. + * + * @param biome {@link Biome}. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder biome(Biome biome) { + return biome(BiomeAPI.getBiomeKey(biome)); + } + + /** + * Set biome surface with specified {@link BlockState}. Example - block of grass in the Overworld biomes + * + * @param state {@link BlockState} for the ground cover. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder surface(BlockState state) { + entryInstance = getFromCache("surface_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, rule); + return new SurfaceRuleEntry(2, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Set biome subsurface with specified {@link BlockState}. Example - dirt in the Overworld biomes. + * + * @param state {@link BlockState} for the subterrain layer. + * @param depth block layer depth. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder subsurface(BlockState state, int depth) { + entryInstance = getFromCache("subsurface_" + depth + "_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(depth, false, CaveSurface.FLOOR), rule); + return new SurfaceRuleEntry(3, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Set biome filler with specified {@link BlockState}. Example - stone in the Overworld biomes. The rule is added with priority 10. + * + * @param state {@link BlockState} for filling. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder filler(BlockState state) { + entryInstance = getFromCache("fill_" + state.toString(), + () -> new SurfaceRuleEntry(10, SurfaceRules.state(state))); + rules.add(entryInstance); + return this; + } + + /** + * Set biome floor with specified {@link BlockState}. Example - underside of a gravel floor. The rule is added with priority 3. + * + * @param state {@link BlockState} for the ground cover. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder floor(BlockState state) { + entryInstance = getFromCache("floor_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + return new SurfaceRuleEntry(3, SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, rule)); + }); + rules.add(entryInstance); + return this; + } + + /** + * Set biome floor material with specified {@link BlockState} and height. The rule is added with priority 3. + * + * @param state {@link BlockState} for the subterrain layer. + * @param height block layer height. + * @param noise The noise object that is applied + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder belowFloor(BlockState state, int height, NoiseCondition noise) { + entryInstance = getFromCache("below_floor_" + height + "_" + state.toString() + "_" + noise.getClass() + .getSimpleName(), + () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(height, + false, + CaveSurface.FLOOR), + SurfaceRules.ifTrue(noise, rule)); + return new SurfaceRuleEntry(3, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Set biome floor material with specified {@link BlockState} and height. The rule is added with priority 3. + * + * @param state {@link BlockState} for the subterrain layer. + * @param height block layer height. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder belowFloor(BlockState state, int height) { + entryInstance = getFromCache("below_floor_" + height + "_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(height, false, CaveSurface.FLOOR), rule); + return new SurfaceRuleEntry(3, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Set biome ceiling with specified {@link BlockState}. Example - block of sandstone in the Overworld desert in air pockets. The rule is added with priority 3. + * + * @param state {@link BlockState} for the ground cover. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder ceil(BlockState state) { + entryInstance = getFromCache("ceil_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + return new SurfaceRuleEntry(3, SurfaceRules.ifTrue(SurfaceRules.ON_CEILING, rule)); + }); + rules.add(entryInstance); + return this; + } + + /** + * Set biome ceiling material with specified {@link BlockState} and height. Example - sandstone in the Overworld deserts. The rule is added with priority 3. + * + * @param state {@link BlockState} for the subterrain layer. + * @param height block layer height. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder aboveCeil(BlockState state, int height) { + entryInstance = getFromCache("above_ceil_" + height + "_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(height, false, CaveSurface.CEILING), rule); + return new SurfaceRuleEntry(3, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Will cover steep areas (with large terrain angle). Example - Overworld mountains. + * + * @param state {@link BlockState} for the steep layer. + * @param depth layer depth + * @return + */ + public SurfaceRuleBuilder steep(BlockState state, int depth) { + entryInstance = getFromCache("steep_" + depth + "_" + state.toString(), () -> { + RuleSource rule = SurfaceRules.state(state); + rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(depth, false, CaveSurface.FLOOR), rule); + rule = SurfaceRules.ifTrue(SurfaceRules.steep(), rule); + int priority = depth < 1 ? 0 : 1; + return new SurfaceRuleEntry(priority, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Allows to add custom rule. + * + * @param priority rule priority, lower values = higher priority (rule will be applied before others). + * @param rule custom {@link SurfaceRules.RuleSource}. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder rule(int priority, SurfaceRules.RuleSource rule) { + rules.add(new SurfaceRuleEntry(priority, rule)); + return this; + } + + /** + * Allows to add custom rule. + * + * @param rule custom {@link SurfaceRules.RuleSource}. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder rule(SurfaceRules.RuleSource rule) { + return rule(7, rule); + } + + /** + * Set biome floor with specified {@link BlockState} and the {@link DoubleBlockSurfaceNoiseCondition}. The rule is added with priority 3. + * + * @param surfaceBlockA {@link BlockState} for the ground cover. + * @param surfaceBlockB {@link BlockState} for the alternative ground cover. + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder chancedFloor(BlockState surfaceBlockA, BlockState surfaceBlockB) { + return chancedFloor(surfaceBlockA, surfaceBlockB, DoubleBlockSurfaceNoiseCondition.CONDITION); + } + + /** + * Set biome floor with specified {@link BlockState} and the given Noise Function. The rule is added with priority 3. + * + * @param surfaceBlockA {@link BlockState} for the ground cover. + * @param surfaceBlockB {@link BlockState} for the alternative ground cover. + * @param noise The {@link NoiseCondition} + * @return same {@link SurfaceRuleBuilder} instance. + */ + public SurfaceRuleBuilder chancedFloor(BlockState surfaceBlockA, BlockState surfaceBlockB, NoiseCondition noise) { + entryInstance = getFromCache("chancedFloor_" + surfaceBlockA + "_" + surfaceBlockB + "_" + noise.getClass() + .getSimpleName(), + () -> { + RuleSource rule = + SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, + SurfaceRules.sequence( + SurfaceRules.ifTrue(noise, + SurfaceRules.state( + surfaceBlockA)), + SurfaceRules.state(surfaceBlockB) + ) + ); + return new SurfaceRuleEntry(4, rule); + }); + rules.add(entryInstance); + return this; + } + + /** + * Finalise rule building process. + * + * @return {@link SurfaceRules.RuleSource}. + */ + public SurfaceRules.RuleSource build() { + Collections.sort(rules); + List ruleList = rules.stream().map(entry -> entry.getRule()).toList(); + SurfaceRules.RuleSource[] ruleArray = ruleList.toArray(new SurfaceRules.RuleSource[ruleList.size()]); + SurfaceRules.RuleSource rule = SurfaceRules.sequence(ruleArray); + if (biomeKey != null) { + rule = SurfaceRules.ifTrue(SurfaceRules.isBiome(biomeKey), rule); + } + return rule; + } + + /** + * Internal function, will take entry from cache or create it if necessary. + * + * @param name {@link String} entry internal name. + * @param supplier {@link Supplier} for {@link SurfaceRuleEntry}. + * @return new or existing {@link SurfaceRuleEntry}. + */ + private static SurfaceRuleEntry getFromCache(String name, Supplier supplier) { + SurfaceRuleEntry entry = RULES_CACHE.get(name); + if (entry == null) { + entry = supplier.get(); + RULES_CACHE.put(name, entry); + } + return entry; + } +} diff --git a/src/main/java/org/betterx/bclib/api/surface/SurfaceRuleEntry.java b/src/main/java/org/betterx/bclib/api/surface/SurfaceRuleEntry.java new file mode 100644 index 00000000..4df2f8e6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/SurfaceRuleEntry.java @@ -0,0 +1,25 @@ +package org.betterx.bclib.api.surface; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.levelgen.SurfaceRules; + +import org.jetbrains.annotations.NotNull; + +public class SurfaceRuleEntry implements Comparable { + private final SurfaceRules.RuleSource rule; + private final byte priority; + + public SurfaceRuleEntry(int priority, SurfaceRules.RuleSource rule) { + this.priority = (byte) priority; + this.rule = rule; + } + + protected SurfaceRules.RuleSource getRule() { + return rule; + } + + @Override + public int compareTo(@NotNull SurfaceRuleEntry entry) { + return Integer.compare(priority, entry.priority); + } +} diff --git a/src/main/java/org/betterx/bclib/api/surface/rules/NoiseCondition.java b/src/main/java/org/betterx/bclib/api/surface/rules/NoiseCondition.java new file mode 100644 index 00000000..835ef866 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/rules/NoiseCondition.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.api.surface.rules; + +import net.minecraft.world.level.levelgen.SurfaceRules; + +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; + +public interface NoiseCondition extends SurfaceRules.ConditionSource { + boolean test(SurfaceRulesContextAccessor context); +} diff --git a/src/main/java/org/betterx/bclib/api/surface/rules/RandomIntProvider.java b/src/main/java/org/betterx/bclib/api/surface/rules/RandomIntProvider.java new file mode 100644 index 00000000..47593ae8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/rules/RandomIntProvider.java @@ -0,0 +1,29 @@ +package org.betterx.bclib.api.surface.rules; + +import net.minecraft.core.Registry; + +import com.mojang.serialization.Codec; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.interfaces.NumericProvider; +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; +import org.betterx.bclib.util.MHelper; + +public record RandomIntProvider(int range) implements NumericProvider { + public static final Codec CODEC = Codec.INT.fieldOf("range") + .xmap(RandomIntProvider::new, obj -> obj.range) + .codec(); + + @Override + public int getNumber(SurfaceRulesContextAccessor context) { + return MHelper.RANDOM.nextInt(range); + } + + @Override + public Codec pcodec() { + return CODEC; + } + + static { + Registry.register(NumericProvider.NUMERIC_PROVIDER, BCLib.makeID("rnd_int"), RandomIntProvider.CODEC); + } +} diff --git a/src/main/java/org/betterx/bclib/api/surface/rules/SurfaceNoiseCondition.java b/src/main/java/org/betterx/bclib/api/surface/rules/SurfaceNoiseCondition.java new file mode 100644 index 00000000..9b02eee3 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/rules/SurfaceNoiseCondition.java @@ -0,0 +1,31 @@ +package org.betterx.bclib.api.surface.rules; + +import net.minecraft.world.level.levelgen.SurfaceRules.Condition; +import net.minecraft.world.level.levelgen.SurfaceRules.Context; +import net.minecraft.world.level.levelgen.SurfaceRules.LazyXZCondition; + +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; + +public abstract class SurfaceNoiseCondition implements NoiseCondition { + @Override + public final Condition apply(Context context2) { + final SurfaceNoiseCondition self = this; + + class Generator extends LazyXZCondition { + Generator() { + super(context2); + } + + @Override + protected boolean compute() { + final SurfaceRulesContextAccessor context = SurfaceRulesContextAccessor.class.cast(this.context); + if (context == null) return false; + return self.test(context); + } + } + + return new Generator(); + } + + public abstract boolean test(SurfaceRulesContextAccessor context); +} diff --git a/src/main/java/org/betterx/bclib/api/surface/rules/SwitchRuleSource.java b/src/main/java/org/betterx/bclib/api/surface/rules/SwitchRuleSource.java new file mode 100644 index 00000000..c864911c --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/rules/SwitchRuleSource.java @@ -0,0 +1,52 @@ +package org.betterx.bclib.api.surface.rules; + +import net.minecraft.core.Registry; +import net.minecraft.util.KeyDispatchDataCodec; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.SurfaceRules.Context; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; +import net.minecraft.world.level.levelgen.SurfaceRules.SurfaceRule; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.betterx.bclib.interfaces.NumericProvider; +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; + +import java.util.List; +import org.jetbrains.annotations.Nullable; + +// +public record SwitchRuleSource(NumericProvider selector, List collection) implements RuleSource { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + NumericProvider.CODEC.fieldOf("selector").forGetter(SwitchRuleSource::selector), + RuleSource.CODEC.listOf().fieldOf("collection").forGetter(SwitchRuleSource::collection) + ) + .apply(instance, + SwitchRuleSource::new)); + + private static final KeyDispatchDataCodec KEY_CODEC = KeyDispatchDataCodec.of(SwitchRuleSource.CODEC); + + @Override + public KeyDispatchDataCodec codec() { + return KEY_CODEC; + } + + @Override + public SurfaceRule apply(Context context) { + + return new SurfaceRule() { + @Nullable + @Override + public BlockState tryApply(int x, int y, int z) { + final SurfaceRulesContextAccessor ctx = SurfaceRulesContextAccessor.class.cast(context); + int nr = Math.max(0, selector.getNumber(ctx)) % collection.size(); + + return collection.get(nr).apply(context).tryApply(x, y, z); + } + }; + } + + static { + Registry.register(Registry.RULE, "bclib_switch_rule", SwitchRuleSource.CODEC); + } +} diff --git a/src/main/java/org/betterx/bclib/api/surface/rules/VolumeNoiseCondition.java b/src/main/java/org/betterx/bclib/api/surface/rules/VolumeNoiseCondition.java new file mode 100644 index 00000000..4adc57f8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/surface/rules/VolumeNoiseCondition.java @@ -0,0 +1,41 @@ +package org.betterx.bclib.api.surface.rules; + +import net.minecraft.util.KeyDispatchDataCodec; +import net.minecraft.world.level.levelgen.SurfaceRules.Condition; +import net.minecraft.world.level.levelgen.SurfaceRules.ConditionSource; +import net.minecraft.world.level.levelgen.SurfaceRules.Context; +import net.minecraft.world.level.levelgen.SurfaceRules.LazyCondition; + +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; + +public abstract class VolumeNoiseCondition implements NoiseCondition { + public abstract KeyDispatchDataCodec codec(); + + @Override + public final Condition apply(Context context2) { + final VolumeNoiseCondition self = this; + + class Generator extends LazyCondition { + Generator() { + super(context2); + } + + @Override + protected long getContextLastUpdate() { + final SurfaceRulesContextAccessor ctx = SurfaceRulesContextAccessor.class.cast(this.context); + return ctx.getLastUpdateY() + ctx.getLastUpdateXZ(); + } + + @Override + protected boolean compute() { + final SurfaceRulesContextAccessor context = SurfaceRulesContextAccessor.class.cast(this.context); + if (context == null) return false; + return self.test(context); + } + } + + return new Generator(); + } + + public abstract boolean test(SurfaceRulesContextAccessor context); +} diff --git a/src/main/java/ru/bclib/api/tag/CommonBiomeTags.java b/src/main/java/org/betterx/bclib/api/tag/CommonBiomeTags.java similarity index 74% rename from src/main/java/ru/bclib/api/tag/CommonBiomeTags.java rename to src/main/java/org/betterx/bclib/api/tag/CommonBiomeTags.java index 4635bc4e..ad2f720e 100644 --- a/src/main/java/ru/bclib/api/tag/CommonBiomeTags.java +++ b/src/main/java/org/betterx/bclib/api/tag/CommonBiomeTags.java @@ -1,8 +1,7 @@ -package ru.bclib.api.tag; +package org.betterx.bclib.api.tag; import net.minecraft.tags.TagKey; import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; public class CommonBiomeTags { public static final TagKey IN_NETHER = TagAPI.makeCommonBiomeTag("in_nether"); diff --git a/src/main/java/org/betterx/bclib/api/tag/CommonBlockTags.java b/src/main/java/org/betterx/bclib/api/tag/CommonBlockTags.java new file mode 100644 index 00000000..225ab8ad --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/CommonBlockTags.java @@ -0,0 +1,27 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Block; + +public class CommonBlockTags { + public static final TagKey BARREL = TagAPI.makeCommonBlockTag("barrel"); + public static final TagKey BOOKSHELVES = TagAPI.makeCommonBlockTag("bookshelves"); + public static final TagKey CHEST = TagAPI.makeCommonBlockTag("chest"); + public static final TagKey END_STONES = TagAPI.makeCommonBlockTag("end_stones"); + public static final TagKey GEN_END_STONES = END_STONES; + public static final TagKey IMMOBILE = TagAPI.makeCommonBlockTag("immobile"); + public static final TagKey LEAVES = TagAPI.makeCommonBlockTag("leaves"); + public static final TagKey NETHERRACK = TagAPI.makeCommonBlockTag("netherrack"); + public static final TagKey NETHER_MYCELIUM = TagAPI.makeCommonBlockTag("nether_mycelium"); + public static final TagKey NETHER_PORTAL_FRAME = TagAPI.makeCommonBlockTag("nether_pframe"); + public static final TagKey NETHER_STONES = TagAPI.makeCommonBlockTag("nether_stones"); + public static final TagKey SAPLINGS = TagAPI.makeCommonBlockTag("saplings"); + public static final TagKey SOUL_GROUND = TagAPI.makeCommonBlockTag("soul_ground"); + public static final TagKey WOODEN_BARREL = TagAPI.makeCommonBlockTag("wooden_barrels"); + public static final TagKey WOODEN_CHEST = TagAPI.makeCommonBlockTag("wooden_chests"); + public static final TagKey WORKBENCHES = TagAPI.makeCommonBlockTag("workbench"); + + public static final TagKey DRAGON_IMMUNE = TagAPI.makeCommonBlockTag("dragon_immune"); + + public static final TagKey MINABLE_WITH_HAMMER = TagAPI.makeCommonBlockTag("mineable/hammer"); +} diff --git a/src/main/java/org/betterx/bclib/api/tag/CommonItemTags.java b/src/main/java/org/betterx/bclib/api/tag/CommonItemTags.java new file mode 100644 index 00000000..669cf6a0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/CommonItemTags.java @@ -0,0 +1,19 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; + +public class CommonItemTags { + public final static TagKey HAMMERS = TagAPI.makeCommonItemTag("hammers"); + public static final TagKey BARREL = TagAPI.makeCommonItemTag("barrel"); + public static final TagKey CHEST = TagAPI.makeCommonItemTag("chest"); + public static final TagKey SHEARS = TagAPI.makeCommonItemTag("shears"); + public static final TagKey FURNACES = TagAPI.makeCommonItemTag("furnaces"); + public static final TagKey IRON_INGOTS = TagAPI.makeCommonItemTag("iron_ingots"); + public static final TagKey LEAVES = TagAPI.makeCommonItemTag("leaves"); + public static final TagKey SAPLINGS = TagAPI.makeCommonItemTag("saplings"); + public static final TagKey SOUL_GROUND = TagAPI.makeCommonItemTag("soul_ground"); + public static final TagKey WOODEN_BARREL = TagAPI.makeCommonItemTag("wooden_barrels"); + public static final TagKey WOODEN_CHEST = TagAPI.makeCommonItemTag("wooden_chests"); + public static final TagKey WORKBENCHES = TagAPI.makeCommonItemTag("workbench"); +} diff --git a/src/main/java/org/betterx/bclib/api/tag/NamedBlockTags.java b/src/main/java/org/betterx/bclib/api/tag/NamedBlockTags.java new file mode 100644 index 00000000..596196de --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/NamedBlockTags.java @@ -0,0 +1,39 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Block; + + +public class NamedBlockTags { + public static final TagKey ANVIL = BlockTags.ANVIL; + public static final TagKey BUTTONS = BlockTags.BUTTONS; + public static final TagKey CLIMBABLE = BlockTags.CLIMBABLE; + public static final TagKey DOORS = BlockTags.DOORS; + public static final TagKey FENCES = BlockTags.FENCES; + public static final TagKey FENCE_GATES = BlockTags.FENCE_GATES; + public static final TagKey LEAVES = BlockTags.LEAVES; + public static final TagKey LOGS = BlockTags.LOGS; + public static final TagKey LOGS_THAT_BURN = BlockTags.LOGS_THAT_BURN; + public static final TagKey NYLIUM = BlockTags.NYLIUM; + public static final TagKey PLANKS = BlockTags.PLANKS; + public static final TagKey PRESSURE_PLATES = BlockTags.PRESSURE_PLATES; + public static final TagKey SAPLINGS = BlockTags.SAPLINGS; + public static final TagKey SIGNS = BlockTags.SIGNS; + public static final TagKey SLABS = BlockTags.SLABS; + public static final TagKey STAIRS = BlockTags.STAIRS; + public static final TagKey STONE_PRESSURE_PLATES = BlockTags.STONE_PRESSURE_PLATES; + public static final TagKey TRAPDOORS = BlockTags.TRAPDOORS; + public static final TagKey WALLS = BlockTags.WALLS; + public static final TagKey WOODEN_BUTTONS = BlockTags.WOODEN_BUTTONS; + public static final TagKey WOODEN_DOORS = BlockTags.WOODEN_DOORS; + public static final TagKey WOODEN_FENCES = BlockTags.WOODEN_FENCES; + public static final TagKey WOODEN_PRESSURE_PLATES = BlockTags.WOODEN_PRESSURE_PLATES; + public static final TagKey WOODEN_SLABS = BlockTags.WOODEN_SLABS; + public static final TagKey WOODEN_STAIRS = BlockTags.WOODEN_STAIRS; + public static final TagKey WOODEN_TRAPDOORS = BlockTags.WOODEN_TRAPDOORS; + public static final TagKey SOUL_FIRE_BASE_BLOCKS = BlockTags.SOUL_FIRE_BASE_BLOCKS; + public static final TagKey SOUL_SPEED_BLOCKS = BlockTags.SOUL_SPEED_BLOCKS; + public static final TagKey BEACON_BASE_BLOCKS = BlockTags.BEACON_BASE_BLOCKS; + public static final TagKey STONE_BRICKS = BlockTags.STONE_BRICKS; +} diff --git a/src/main/java/org/betterx/bclib/api/tag/NamedItemTags.java b/src/main/java/org/betterx/bclib/api/tag/NamedItemTags.java new file mode 100644 index 00000000..49d08d92 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/NamedItemTags.java @@ -0,0 +1,32 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; + + +public class NamedItemTags { + public static final TagKey BUTTONS = ItemTags.BUTTONS; + public static final TagKey DOORS = ItemTags.DOORS; + public static final TagKey FENCES = ItemTags.FENCES; + public static final TagKey LEAVES = ItemTags.LEAVES; + public static final TagKey LOGS = ItemTags.LOGS; + public static final TagKey LOGS_THAT_BURN = ItemTags.LOGS_THAT_BURN; + public static final TagKey PLANKS = ItemTags.PLANKS; + public static final TagKey SAPLINGS = ItemTags.SAPLINGS; + public static final TagKey SIGNS = ItemTags.SIGNS; + public static final TagKey SLABS = ItemTags.SLABS; + public static final TagKey STAIRS = ItemTags.STAIRS; + public static final TagKey TRAPDOORS = ItemTags.TRAPDOORS; + public static final TagKey WOODEN_BUTTONS = ItemTags.WOODEN_BUTTONS; + public static final TagKey WOODEN_DOORS = ItemTags.WOODEN_DOORS; + public static final TagKey WOODEN_FENCES = ItemTags.WOODEN_FENCES; + public static final TagKey WOODEN_PRESSURE_PLATES = ItemTags.WOODEN_PRESSURE_PLATES; + public static final TagKey WOODEN_SLABS = ItemTags.WOODEN_SLABS; + public static final TagKey WOODEN_STAIRS = ItemTags.WOODEN_STAIRS; + public static final TagKey WOODEN_TRAPDOORS = ItemTags.WOODEN_TRAPDOORS; + public static final TagKey BEACON_PAYMENT_ITEMS = ItemTags.BEACON_PAYMENT_ITEMS; + public static final TagKey STONE_BRICKS = ItemTags.STONE_BRICKS; + public static final TagKey STONE_CRAFTING_MATERIALS = ItemTags.STONE_CRAFTING_MATERIALS; + public static final TagKey STONE_TOOL_MATERIALS = ItemTags.STONE_TOOL_MATERIALS; +} diff --git a/src/main/java/org/betterx/bclib/api/tag/NamedMineableTags.java b/src/main/java/org/betterx/bclib/api/tag/NamedMineableTags.java new file mode 100644 index 00000000..1d743852 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/NamedMineableTags.java @@ -0,0 +1,16 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Block; + + +public class NamedMineableTags { + public static final TagKey AXE = BlockTags.MINEABLE_WITH_AXE; + public static final TagKey HOE = BlockTags.MINEABLE_WITH_HOE; + public static final TagKey PICKAXE = BlockTags.MINEABLE_WITH_PICKAXE; + public static final TagKey SHEARS = TagAPI.makeBlockTag("fabric", "mineable/shears"); + public static final TagKey SHOVEL = BlockTags.MINEABLE_WITH_SHOVEL; + public static final TagKey SWORD = TagAPI.makeBlockTag("fabric", "mineable/sword"); + public static final TagKey HAMMER = TagAPI.makeCommonBlockTag("mineable/hammer"); +} diff --git a/src/main/java/org/betterx/bclib/api/tag/NamedToolTags.java b/src/main/java/org/betterx/bclib/api/tag/NamedToolTags.java new file mode 100644 index 00000000..6956cfb6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/NamedToolTags.java @@ -0,0 +1,14 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; + + +public class NamedToolTags { + public static final TagKey FABRIC_AXES = TagAPI.makeItemTag("fabric", "axes"); + public static final TagKey FABRIC_HOES = TagAPI.makeItemTag("fabric", "hoes"); + public static final TagKey FABRIC_PICKAXES = TagAPI.makeItemTag("fabric", "pickaxes"); + public static final TagKey FABRIC_SHEARS = TagAPI.makeItemTag("fabric", "shears"); + public static final TagKey FABRIC_SHOVELS = TagAPI.makeItemTag("fabric", "shovels"); + public static final TagKey FABRIC_SWORDS = TagAPI.makeItemTag("fabric", "swords"); +} diff --git a/src/main/java/org/betterx/bclib/api/tag/TagAPI.java b/src/main/java/org/betterx/bclib/api/tag/TagAPI.java new file mode 100644 index 00000000..6981ccb7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/tag/TagAPI.java @@ -0,0 +1,277 @@ +package org.betterx.bclib.api.tag; + +import net.minecraft.core.DefaultedRegistry; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.tags.Tag; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; + +import com.google.common.collect.Maps; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.mixin.common.DiggerItemAccessor; + +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +public class TagAPI { + private static final Map> TYPES = Maps.newHashMap(); + + public static TagType.RegistryBacked BLOCKS = registerType(Registry.BLOCK); + public static TagType.RegistryBacked ITEMS = registerType(Registry.ITEM); + public static TagType.Simple BIOMES = registerType(Registry.BIOME_REGISTRY, + "tags/worldgen/biome", + b -> BiomeAPI.getBiomeID(b)); + + private static TagType.RegistryBacked registerType(DefaultedRegistry registry) { + TagType type = new TagType.RegistryBacked<>(registry); + return (TagType.RegistryBacked) TYPES.computeIfAbsent(type.directory, (dir) -> type); + } + + public static TagType.Simple registerType(ResourceKey> registry, + String directory, + Function locationProvider) { + return (TagType.Simple) TYPES.computeIfAbsent(directory, + (dir) -> new TagType.Simple<>(registry, + dir, + locationProvider)); + } + + public static TagType.UnTyped registerType(ResourceKey> registry, String directory) { + return (TagType.UnTyped) TYPES.computeIfAbsent(directory, (dir) -> new TagType.UnTyped<>(registry, dir)); + } + + /** + * Get or create {@link Block} {@link TagKey} with mod namespace. + * + * @param modID - {@link String} mod namespace (mod id); + * @param name - {@link String} tag name. + * @return {@link Block} {@link TagKey}. + */ + public static TagKey makeBiomeTag(String modID, String name) { + return BIOMES.makeTag(new ResourceLocation(modID, name)); + } + + + /** + * Get or create {@link Block} {@link TagKey} with mod namespace. + * + * @param modID - {@link String} mod namespace (mod id); + * @param name - {@link String} tag name. + * @return {@link Block} {@link TagKey}. + */ + public static TagKey makeBlockTag(String modID, String name) { + return BLOCKS.makeTag(new ResourceLocation(modID, name)); + } + + /** + * Get or create {@link Block} {@link TagKey} with mod namespace. + * + * @param id - {@link String} id for the tag; + * @return {@link Block} {@link TagKey}. + */ + public static TagKey makeBlockTag(ResourceLocation id) { + return BLOCKS.makeTag(id); + } + + /** + * Get or create {@link Item} {@link TagKey} with mod namespace. + * + * @param modID - {@link String} mod namespace (mod id); + * @param name - {@link String} tag name. + * @return {@link Item} {@link TagKey}. + */ + public static TagKey makeItemTag(String modID, String name) { + return ITEMS.makeTag(new ResourceLocation(modID, name)); + } + + /** + * Get or create {@link Item} {@link TagKey} with mod namespace. + * + * @param id - {@link String} id for the tag; + * @return {@link Item} {@link TagKey}. + */ + public static TagKey makeItemTag(ResourceLocation id) { + return ITEMS.makeTag(id); + } + + /** + * Get or create {@link Block} {@link TagKey}. + * + * @param name - {@link String} tag name. + * @return {@link Block} {@link TagKey}. + * @see Fabric Wiki (Tags) + */ + public static TagKey makeCommonBlockTag(String name) { + return BLOCKS.makeCommonTag(name); + } + + /** + * Get or create {@link Item} {@link TagKey}. + * + * @param name - {@link String} tag name. + * @return {@link Item} {@link TagKey}. + * @see Fabric Wiki (Tags) + */ + public static TagKey makeCommonItemTag(String name) { + return ITEMS.makeCommonTag(name); + } + + public static TagKey makeCommonBiomeTag(String name) { + return BIOMES.makeCommonTag(name); + } + + /** + * Initializes basic tags. Should be called only in BCLib main class. + */ + public static void init() { + addBlockTag(CommonBlockTags.BOOKSHELVES, Blocks.BOOKSHELF); + addBlockTag(CommonBlockTags.CHEST, Blocks.CHEST); + addItemTag(CommonItemTags.CHEST, Items.CHEST); + addItemTag(CommonItemTags.IRON_INGOTS, Items.IRON_INGOT); + addItemTag(CommonItemTags.FURNACES, Blocks.FURNACE); + } + + /** + * Adds multiple Tags to one Biome. + * + * @param tagIDs array of {@link TagKey} tag IDs. + * @param biome The {@link Biome} to add tag. + */ + @SafeVarargs + @Deprecated(forRemoval = true) + public static void addBiomeTags(Biome biome, TagKey... tagIDs) { + BIOMES.add(biome, tagIDs); + } + + /** + * Adds one Tag to multiple Biomes. + * + * @param tagID {@link TagKey} tag ID. + * @param biomes array of {@link Biome} to add into tag. + */ + public static void addBiomeTag(TagKey tagID, Biome... biomes) { + BIOMES.add(tagID, biomes); + } + + + /** + * Adds multiple Tags to one Block. + * + * @param tagIDs array of {@link TagKey} tag IDs. + * @param block The {@link Block} to add tag. + */ + @SafeVarargs + @Deprecated(forRemoval = true) + public static void addBlockTags(Block block, TagKey... tagIDs) { + BLOCKS.add(block, tagIDs); + } + + + /** + * Adds one Tag to multiple Blocks. + * + * @param tagID {@link TagKey} tag ID. + * @param blocks array of {@link Block} to add into tag. + */ + public static void addBlockTag(TagKey tagID, Block... blocks) { + BLOCKS.add(tagID, blocks); + } + + /** + * Adds multiple Tags to one Item. + * + * @param tagIDs array of {@link TagKey} tag IDs. + * @param item The {@link Item} to add tag. + */ + @Deprecated(forRemoval = true) + @SafeVarargs + public static void addItemTags(ItemLike item, TagKey... tagIDs) { + ITEMS.add(item.asItem(), tagIDs); + } + + /** + * Adds one Tag to multiple Items. + * + * @param tagID {@link TagKey} tag ID. + * @param items array of {@link ItemLike} to add into tag. + */ + public static void addItemTag(TagKey tagID, ItemLike... items) { + for (ItemLike i : items) { + ITEMS.add(i.asItem(), tagID); + } + } + + /** + * Adds one Tag to multiple Items. + * + * @param tagID {@link TagKey} tag ID. + * @param items array of {@link ItemLike} to add into tag. + */ + public static void addItemTag(TagKey tagID, Item... items) { + ITEMS.add(tagID, items); + } + + + /** + * Automatically called in {@link net.minecraft.tags.TagLoader#loadAndBuild(ResourceManager)}. + *

+ * In most cases there is no need to call this Method manually. + * + * @param directory The name of the Tag-directory. Should be either "tags/blocks" or + * "tags/items". + * @param tagsMap The map that will hold the registered Tags + * @return The {@code tagsMap} Parameter. + */ + public static Map apply(String directory, + Map tagsMap) { + + TagType type = TYPES.get(directory); + if (type != null) { + type.apply(tagsMap); + } + +// final BiConsumer> consumer; +// consumer = (id, ids) -> apply(tagsMap.computeIfAbsent(id, key -> Tag.Builder.tag()), ids); +// +// if ("tags/blocks".equals(directory)) { +// TAGS_BLOCK.forEach(consumer); +// } +// else if ("tags/items".equals(directory)) { +// TAGS_ITEM.forEach(consumer); +// } +// else if ("tags/worldgen/biome".equals(directory)) { +// TAGS_BIOME.forEach(consumer); +// } + return tagsMap; + } + + /** + * Adds all {@code ids} to the {@code builder}. + * + * @param builder + * @param ids + * @return The Builder passed as {@code builder}. + */ + public static Tag.Builder apply(Tag.Builder builder, Set ids) { + ids.forEach(value -> builder.addElement(value, "BCLib Code")); + return builder; + } + + + public static boolean isToolWithMineableTag(ItemStack stack, TagKey tag) { + if (stack.getItem() instanceof DiggerItemAccessor dig) { + return dig.bclib_getBlockTag().equals(tag); + } + return false; + } +} diff --git a/src/main/java/ru/bclib/api/tag/TagType.java b/src/main/java/org/betterx/bclib/api/tag/TagType.java similarity index 87% rename from src/main/java/ru/bclib/api/tag/TagType.java rename to src/main/java/org/betterx/bclib/api/tag/TagType.java index 251ed869..eea39a2e 100644 --- a/src/main/java/ru/bclib/api/tag/TagType.java +++ b/src/main/java/org/betterx/bclib/api/tag/TagType.java @@ -1,4 +1,4 @@ -package ru.bclib.api.tag; +package org.betterx.bclib.api.tag; import net.minecraft.core.DefaultedRegistry; import net.minecraft.core.Registry; @@ -11,8 +11,8 @@ import net.minecraft.world.level.biome.Biome; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import ru.bclib.BCLib; -import ru.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.biomes.BiomeAPI; import java.util.Map; import java.util.Set; @@ -21,14 +21,15 @@ import java.util.function.Function; public class TagType { boolean isFrozen = false; - public static class RegistryBacked extends Simple{ + + public static class RegistryBacked extends Simple { private final DefaultedRegistry registry; RegistryBacked(DefaultedRegistry registry) { super( registry.key(), TagManager.getTagDir(registry.key()), - (T element) -> { + (T element) -> { ResourceLocation id = registry.getKey(element); if (id != registry.getDefaultKey()) { return id; @@ -57,7 +58,7 @@ public class TagType { } public void add(TagKey tagID, T... elements) { - super.add(tagID, elements); + super.add(tagID, elements); } public void add(T element, TagKey... tagIDs) { @@ -77,18 +78,21 @@ public class TagType { public static class UnTyped extends TagType { UnTyped(ResourceKey> registry, - String directory) { - super(registry, directory, (t)->{throw new RuntimeException("Using Untyped TagType with Type-Dependant access. ");}); + String directory) { + super(registry, directory, (t) -> { + throw new RuntimeException("Using Untyped TagType with Type-Dependant access. "); + }); } } + public final String directory; private final Map> tags = Maps.newConcurrentMap(); public final ResourceKey> registryKey; private final Function locationProvider; private TagType(ResourceKey> registry, - String directory, - Function locationProvider) { + String directory, + Function locationProvider) { this.registryKey = registry; this.directory = directory; this.locationProvider = locationProvider; @@ -142,7 +146,8 @@ public class TagType { /** * Adds one Tag to multiple Elements. - * @param tagID {@link TagKey< Biome >} tag ID. + * + * @param tagID {@link TagKey< Biome >} tag ID. * @param elements array of Elements to add into tag. */ protected void add(TagKey tagID, T... elements) { @@ -184,10 +189,11 @@ public class TagType { public void forEach(BiConsumer> consumer) { tags.forEach(consumer); } - public void apply(Map tagsMap){ - if (Registry.BIOME_REGISTRY.equals(registryKey)) BiomeAPI._runTagAdders(); - //this.isFrozen = true; - this.forEach((id, ids) -> TagAPI.apply(tagsMap.computeIfAbsent(id, key -> Tag.Builder.tag()), ids)); + public void apply(Map tagsMap) { + if (Registry.BIOME_REGISTRY.equals(registryKey)) BiomeAPI._runTagAdders(); + + //this.isFrozen = true; + this.forEach((id, ids) -> TagAPI.apply(tagsMap.computeIfAbsent(id, key -> Tag.Builder.tag()), ids)); } } diff --git a/src/main/java/org/betterx/bclib/blockentities/BaseBarrelBlockEntity.java b/src/main/java/org/betterx/bclib/blockentities/BaseBarrelBlockEntity.java new file mode 100644 index 00000000..0dec8f56 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blockentities/BaseBarrelBlockEntity.java @@ -0,0 +1,149 @@ +package org.betterx.bclib.blockentities; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.blocks.BaseBarrelBlock; +import org.betterx.bclib.registry.BaseBlockEntities; + +public class BaseBarrelBlockEntity extends RandomizableContainerBlockEntity { + private NonNullList inventory; + private int viewerCount; + + private BaseBarrelBlockEntity(BlockEntityType type, BlockPos blockPos, BlockState blockState) { + super(type, blockPos, blockState); + this.inventory = NonNullList.withSize(27, ItemStack.EMPTY); + } + + public BaseBarrelBlockEntity(BlockPos blockPos, BlockState blockState) { + this(BaseBlockEntities.BARREL, blockPos, blockState); + } + + @Override + public void saveAdditional(CompoundTag tag) { + super.saveAdditional(tag); + if (!this.trySaveLootTable(tag)) { + ContainerHelper.saveAllItems(tag, this.inventory); + } + + //return tag; + } + + public void load(CompoundTag tag) { + super.load(tag); + this.inventory = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); + if (!this.tryLoadLootTable(tag)) { + ContainerHelper.loadAllItems(tag, this.inventory); + } + } + + public int getContainerSize() { + return 27; + } + + protected NonNullList getItems() { + return this.inventory; + } + + protected void setItems(NonNullList list) { + this.inventory = list; + } + + protected Component getDefaultName() { + return Component.translatable("container.barrel"); + } + + protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) { + return ChestMenu.threeRows(syncId, playerInventory, this); + } + + public void startOpen(Player player) { + if (!player.isSpectator()) { + if (viewerCount < 0) { + viewerCount = 0; + } + + ++viewerCount; + BlockState blockState = this.getBlockState(); + if (!blockState.getValue(BarrelBlock.OPEN)) { + playSound(blockState, SoundEvents.BARREL_OPEN); + setOpen(blockState, true); + } + + if (level != null) { + scheduleUpdate(); + } + } + } + + private void scheduleUpdate() { + level.scheduleTick(getBlockPos(), getBlockState().getBlock(), 5); + } + + public void tick() { + if (level != null) { + viewerCount = ChestBlockEntity.getOpenCount(level, worldPosition); + if (viewerCount > 0) { + scheduleUpdate(); + } else { + BlockState blockState = getBlockState(); + if (!(blockState.getBlock() instanceof BaseBarrelBlock)) { + setRemoved(); + return; + } + if (blockState.getValue(BarrelBlock.OPEN)) { + playSound(blockState, SoundEvents.BARREL_CLOSE); + setOpen(blockState, false); + } + } + } + } + + public void stopOpen(Player player) { + if (!player.isSpectator()) { + --this.viewerCount; + } + } + + private void setOpen(BlockState state, boolean open) { + if (level != null) { + level.setBlock(this.getBlockPos(), state.setValue(BarrelBlock.OPEN, open), 3); + } + } + + private void playSound(BlockState blockState, SoundEvent soundEvent) { + if (level != null) { + Vec3i vec3i = blockState.getValue(BarrelBlock.FACING).getNormal(); + double d = (double) this.worldPosition.getX() + 0.5D + (double) vec3i.getX() / 2.0D; + double e = (double) this.worldPosition.getY() + 0.5D + (double) vec3i.getY() / 2.0D; + double f = (double) this.worldPosition.getZ() + 0.5D + (double) vec3i.getZ() / 2.0D; + level.playSound( + null, + d, + e, + f, + soundEvent, + SoundSource.BLOCKS, + 0.5F, + this.level.random.nextFloat() * 0.1F + 0.9F + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/blockentities/BaseChestBlockEntity.java b/src/main/java/org/betterx/bclib/blockentities/BaseChestBlockEntity.java new file mode 100644 index 00000000..c574ade0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blockentities/BaseChestBlockEntity.java @@ -0,0 +1,13 @@ +package org.betterx.bclib.blockentities; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.registry.BaseBlockEntities; + +public class BaseChestBlockEntity extends ChestBlockEntity { + public BaseChestBlockEntity(BlockPos blockPos, BlockState blockState) { + super(BaseBlockEntities.CHEST, blockPos, blockState); + } +} diff --git a/src/main/java/org/betterx/bclib/blockentities/BaseFurnaceBlockEntity.java b/src/main/java/org/betterx/bclib/blockentities/BaseFurnaceBlockEntity.java new file mode 100644 index 00000000..c8ee24e4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blockentities/BaseFurnaceBlockEntity.java @@ -0,0 +1,26 @@ +package org.betterx.bclib.blockentities; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.FurnaceMenu; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.registry.BaseBlockEntities; + +public class BaseFurnaceBlockEntity extends AbstractFurnaceBlockEntity { + public BaseFurnaceBlockEntity(BlockPos blockPos, BlockState blockState) { + super(BaseBlockEntities.FURNACE, blockPos, blockState, RecipeType.SMELTING); + } + + protected Component getDefaultName() { + return Component.translatable("container.furnace"); + } + + protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) { + return new FurnaceMenu(syncId, playerInventory, this, this.dataAccess); + } +} diff --git a/src/main/java/org/betterx/bclib/blockentities/BaseSignBlockEntity.java b/src/main/java/org/betterx/bclib/blockentities/BaseSignBlockEntity.java new file mode 100644 index 00000000..ef88b359 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blockentities/BaseSignBlockEntity.java @@ -0,0 +1,19 @@ +package org.betterx.bclib.blockentities; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.registry.BaseBlockEntities; + +public class BaseSignBlockEntity extends SignBlockEntity { + public BaseSignBlockEntity(BlockPos blockPos, BlockState blockState) { + super(blockPos, blockState); + } + + @Override + public BlockEntityType getType() { + return BaseBlockEntities.SIGN; + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/blockentities/DynamicBlockEntityType.java b/src/main/java/org/betterx/bclib/blockentities/DynamicBlockEntityType.java new file mode 100644 index 00000000..93a7afa9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blockentities/DynamicBlockEntityType.java @@ -0,0 +1,44 @@ +package org.betterx.bclib.blockentities; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +import com.google.common.collect.Sets; + +import java.util.Collections; +import java.util.Set; +import org.jetbrains.annotations.Nullable; + +public class DynamicBlockEntityType extends BlockEntityType { + + private final Set validBlocks = Sets.newHashSet(); + private final BlockEntitySupplier factory; + + public DynamicBlockEntityType(BlockEntitySupplier supplier) { + super(null, Collections.emptySet(), null); + this.factory = supplier; + } + + @Override + @Nullable + public T create(BlockPos blockPos, BlockState blockState) { + return factory.create(blockPos, blockState); + } + + @Override + public boolean isValid(BlockState blockState) { + return validBlocks.contains(blockState.getBlock()); + } + + public void registerBlock(Block block) { + validBlocks.add(block); + } + + @FunctionalInterface + public interface BlockEntitySupplier { + T create(BlockPos blockPos, BlockState blockState); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseAnvilBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseAnvilBlock.java new file mode 100644 index 00000000..d7026e80 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseAnvilBlock.java @@ -0,0 +1,138 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.PickaxeItem; +import net.minecraft.world.level.block.AnvilBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.CustomItemProvider; +import org.betterx.bclib.items.BaseAnvilItem; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public abstract class BaseAnvilBlock extends AnvilBlock implements BlockModelProvider, CustomItemProvider { + public static final IntegerProperty DESTRUCTION = BlockProperties.DESTRUCTION; + public IntegerProperty durability; + + public BaseAnvilBlock(MaterialColor color) { + this(FabricBlockSettings.copyOf(Blocks.ANVIL).color(color)); + } + + public BaseAnvilBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + if (getMaxDurability() != 3) { + durability = IntegerProperty.create("durability", 0, getMaxDurability()); + } else { + durability = BlockProperties.DEFAULT_ANVIL_DURABILITY; + } + builder.add(DESTRUCTION, durability); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + int destruction = blockState.getValue(DESTRUCTION); + String name = blockId.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%modid%", blockId.getNamespace()); + textures.put("%anvil%", name); + textures.put("%top%", name + "_top_" + destruction); + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_ANVIL, textures); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + int destruction = blockState.getValue(DESTRUCTION); + String modId = stateId.getNamespace(); + String modelId = "block/" + stateId.getPath() + "_top_" + destruction; + ResourceLocation modelLocation = new ResourceLocation(modId, modelId); + registerBlockModel(stateId, modelLocation, blockState, modelCache); + return ModelsHelper.createFacingModel(modelLocation, blockState.getValue(FACING), false, false); + } + + @Override + public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { + return new BaseAnvilItem(this, settings); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + int destruction = state.getValue(DESTRUCTION); + int durability = state.getValue(getDurabilityProp()); + int value = destruction * getMaxDurability() + durability; + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.getItem() instanceof PickaxeItem) { + ItemStack itemStack = new ItemStack(this); + itemStack.getOrCreateTag().putInt(BaseAnvilItem.DESTRUCTION, value); + return Lists.newArrayList(itemStack); + } + return Collections.emptyList(); + } + + public IntegerProperty getDurabilityProp() { + return durability; + } + + public int getMaxDurability() { + return 3; + } + + public BlockState damageAnvilUse(BlockState state, RandomSource random) { + IntegerProperty durability = getDurabilityProp(); + int value = state.getValue(durability); + if (value < getMaxDurability() && random.nextInt(8) == 0) { + return state.setValue(durability, value + 1); + } + value = state.getValue(DESTRUCTION); + return value < 2 ? state.setValue(DESTRUCTION, value + 1).setValue(durability, 0) : null; + } + + public BlockState damageAnvilFall(BlockState state) { + int destruction = state.getValue(DESTRUCTION); + return destruction < 2 ? state.setValue(DESTRUCTION, destruction + 1) : null; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseAttachedBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseAttachedBlock.java new file mode 100644 index 00000000..9da9972d --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseAttachedBlock.java @@ -0,0 +1,81 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; + +import org.betterx.bclib.util.BlocksHelper; + +@SuppressWarnings("deprecation") +public abstract class BaseAttachedBlock extends BaseBlockNotFull { + public static final DirectionProperty FACING = BlockStateProperties.FACING; + + public BaseAttachedBlock(Properties settings) { + super(settings); + registerDefaultState(defaultBlockState().setValue(FACING, Direction.UP)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + BlockState blockState = defaultBlockState(); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + Direction[] directions = ctx.getNearestLookingDirections(); + for (Direction direction : directions) { + Direction direction2 = direction.getOpposite(); + blockState = blockState.setValue(FACING, direction2); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState; + } + } + return null; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + Direction direction = state.getValue(FACING); + BlockPos blockPos = pos.relative(direction.getOpposite()); + return canSupportCenter(world, blockPos, direction) || world.getBlockState(blockPos).is(BlockTags.LEAVES); + } + + @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 BlockState rotate(BlockState state, Rotation rotation) { + return BlocksHelper.rotateHorizontal(state, rotation, FACING); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return BlocksHelper.mirrorHorizontal(state, mirror, FACING); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseBarkBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseBarkBlock.java new file mode 100644 index 00000000..b2e6ab92 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseBarkBlock.java @@ -0,0 +1,26 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.PatternsHelper; + +import java.util.Optional; + +public class BaseBarkBlock extends BaseRotatedPillarBlock { + public BaseBarkBlock(Properties settings) { + super(settings); + } + + @Override + protected Optional createBlockPattern(ResourceLocation blockId) { + blockId = Registry.BLOCK.getKey(this); + return PatternsHelper.createJson(BasePatterns.BLOCK_BASE, replacePath(blockId)); + } + + private ResourceLocation replacePath(ResourceLocation blockId) { + String newPath = blockId.getPath().replace("_bark", "_log_side"); + return new ResourceLocation(blockId.getNamespace(), newPath); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseBarrelBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseBarrelBlock.java new file mode 100644 index 00000000..6b6ae1e2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseBarrelBlock.java @@ -0,0 +1,159 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.stats.Stats; +import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.monster.piglin.PiglinAi; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.phys.BlockHitResult; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.blockentities.BaseBarrelBlockEntity; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.registry.BaseBlockEntities; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseBarrelBlock extends BarrelBlock implements BlockModelProvider { + public BaseBarrelBlock(Block source) { + this(FabricBlockSettings.copyOf(source).noOcclusion()); + } + + public BaseBarrelBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return BaseBlockEntities.BARREL.create(blockPos, blockState); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + List drop = super.getDrops(state, builder); + drop.add(new ItemStack(this.asItem())); + return drop; + } + + @Override + public InteractionResult use(BlockState state, + Level world, + BlockPos pos, + Player player, + InteractionHand hand, + BlockHitResult hit) { + if (world.isClientSide) { + return InteractionResult.SUCCESS; + } else { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof BaseBarrelBlockEntity) { + player.openMenu((BaseBarrelBlockEntity) blockEntity); + player.awardStat(Stats.OPEN_BARREL); + PiglinAi.angerNearbyPiglins(player, true); + } + + return InteractionResult.CONSUME; + } + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof BaseBarrelBlockEntity) { + ((BaseBarrelBlockEntity) blockEntity).tick(); + } + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + if (itemStack.hasCustomHoverName()) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof BaseBarrelBlockEntity) { + ((BaseBarrelBlockEntity) blockEntity).setCustomName(itemStack.getHoverName()); + } + } + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern; + if (blockState.getValue(OPEN)) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BARREL_OPEN, blockId); + } else { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BOTTOM_TOP, blockId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + String open = blockState.getValue(OPEN) ? "_open" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + open); + registerBlockModel(stateId, modelId, blockState, modelCache); + Direction facing = blockState.getValue(FACING); + BlockModelRotation rotation = BlockModelRotation.X0_Y0; + switch (facing) { + case NORTH: + rotation = BlockModelRotation.X90_Y0; + break; + case EAST: + rotation = BlockModelRotation.X90_Y90; + break; + case SOUTH: + rotation = BlockModelRotation.X90_Y180; + break; + case WEST: + rotation = BlockModelRotation.X90_Y270; + break; + case DOWN: + rotation = BlockModelRotation.X180_Y0; + break; + default: + break; + } + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseBlock.java new file mode 100644 index 00000000..fa6199bb --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseBlock.java @@ -0,0 +1,76 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +/** + * Base class for a default Block. + *

+ * This Block-Type will: + *

    + *
  • Drop itself
  • + *
  • Automatically create an Item-Model from the Block-Model
  • + *
+ */ +public class BaseBlock extends Block implements BlockModelProvider { + /** + * Creates a new Block with the passed properties + * + * @param settings The properties of the Block. + */ + public BaseBlock(Properties settings) { + super(settings); + } + + /** + * {@inheritDoc} + *

+ * This implementation will drop the Block itself + */ + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + /** + * {@inheritDoc} + *

+ * This implementation will load the Block-Model and return it as the Item-Model + */ + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + /** + * This method is used internally. + *

+ * It is called from Block-Contructors, to allow the augmentation of the blocks + * preset properties. + *

+ * For example in {@link BaseLeavesBlock#BaseLeavesBlock(Block, MaterialColor, Consumer)} + * + * @param customizeProperties A {@link Consumer} to call with the preset properties + * @param settings The properties as created by the Block + * @return The reconfigured {@code settings} + */ + static FabricBlockSettings acceptAndReturn(Consumer customizeProperties, + FabricBlockSettings settings) { + customizeProperties.accept(settings); + return settings; + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/blocks/BaseBlockNotFull.java b/src/main/java/org/betterx/bclib/blocks/BaseBlockNotFull.java new file mode 100644 index 00000000..22da4b48 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseBlockNotFull.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.state.BlockState; + +public class BaseBlockNotFull extends BaseBlock { + public BaseBlockNotFull(Properties settings) { + super(settings); + } + + public boolean canSuffocate(BlockState state, BlockGetter view, BlockPos pos) { + return false; + } + + public boolean isSimpleFullBlock(BlockState state, BlockGetter view, BlockPos pos) { + return false; + } + + public boolean allowsSpawning(BlockState state, BlockGetter view, BlockPos pos, EntityType type) { + return false; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseBlockWithEntity.java b/src/main/java/org/betterx/bclib/blocks/BaseBlockWithEntity.java new file mode 100644 index 00000000..5900b454 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseBlockWithEntity.java @@ -0,0 +1,28 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import java.util.Collections; +import java.util.List; + +public class BaseBlockWithEntity extends BaseEntityBlock { + public BaseBlockWithEntity(Properties settings) { + super(settings); + } + + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return null; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseBookshelfBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseBookshelfBlock.java new file mode 100644 index 00000000..defae594 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseBookshelfBlock.java @@ -0,0 +1,60 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseBookshelfBlock extends BaseBlock { + public BaseBookshelfBlock(Block source) { + this(FabricBlockSettings.copyOf(source)); + } + + public BaseBookshelfBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null) { + int silk = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool); + if (silk > 0) { + return Collections.singletonList(new ItemStack(this)); + } + } + return Collections.singletonList(new ItemStack(Items.BOOK, 3)); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BOOKSHELF, replacePath(blockId)); + return ModelsHelper.fromPattern(pattern); + } + + private ResourceLocation replacePath(ResourceLocation blockId) { + String newPath = blockId.getPath().replace("_bookshelf", ""); + return new ResourceLocation(blockId.getNamespace(), newPath); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseButtonBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseButtonBlock.java new file mode 100644 index 00000000..5b869b3d --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseButtonBlock.java @@ -0,0 +1,108 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ButtonBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.AttachFace; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public abstract class BaseButtonBlock extends ButtonBlock implements BlockModelProvider { + private final Block parent; + + protected BaseButtonBlock(Block parent, Properties properties, boolean sensitive) { + super(sensitive, properties); + this.parent = parent; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_BUTTON, parentId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = blockState.getValue(POWERED) + ? PatternsHelper.createJson( + BasePatterns.BLOCK_BUTTON_PRESSED, + parentId + ) + : PatternsHelper.createJson(BasePatterns.BLOCK_BUTTON, parentId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + String powered = blockState.getValue(POWERED) ? "_powered" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + powered); + registerBlockModel(stateId, modelId, blockState, modelCache); + AttachFace face = blockState.getValue(FACE); + boolean isCeiling = face == AttachFace.CEILING; + int x = 0, y = 0; + switch (face) { + case CEILING: + x = 180; + break; + case WALL: + x = 90; + break; + default: + break; + } + switch (blockState.getValue(FACING)) { + case NORTH: + if (isCeiling) { + y = 180; + } + break; + case EAST: + y = isCeiling ? 270 : 90; + break; + case SOUTH: + if (!isCeiling) { + y = 180; + } + break; + case WEST: + y = isCeiling ? 90 : 270; + break; + default: + break; + } + BlockModelRotation rotation = BlockModelRotation.by(x, y); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), face == AttachFace.WALL); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseChainBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseChainBlock.java new file mode 100644 index 00000000..a13169b2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseChainBlock.java @@ -0,0 +1,75 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.ChainBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseChainBlock extends ChainBlock implements BlockModelProvider, RenderLayerProvider { + public BaseChainBlock(MaterialColor color) { + this(FabricBlockSettings.copyOf(Blocks.CHAIN).color(color)); + } + + public BaseChainBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return ModelsHelper.createItemModel(blockId); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CHAIN, blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + Direction.Axis axis = blockState.getValue(AXIS); + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRotatedModel(modelId, axis); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseChestBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseChestBlock.java new file mode 100644 index 00000000..127cb36e --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseChestBlock.java @@ -0,0 +1,62 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.registry.BaseBlockEntities; + +import java.util.List; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseChestBlock extends ChestBlock implements BlockModelProvider { + private final Block parent; + + public BaseChestBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion(), () -> BaseBlockEntities.CHEST); + this.parent = source; + } + + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return BaseBlockEntities.CHEST.create(blockPos, blockState); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + List drop = super.getDrops(state, builder); + drop.add(new ItemStack(this.asItem())); + return drop; + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_CHEST, blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + return ModelsHelper.createBlockEmpty(parentId); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseComposterBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseComposterBlock.java new file mode 100644 index 00000000..f2088199 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseComposterBlock.java @@ -0,0 +1,75 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ComposterBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseComposterBlock extends ComposterBlock implements BlockModelProvider { + public BaseComposterBlock(Block source) { + super(FabricBlockSettings.copyOf(source)); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this.asItem())); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_COMPOSTER, blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + + ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); + LEVEL.getPossibleValues().forEach(level -> { + if (level > 0) { + ResourceLocation contentId; + if (level > 7) { + contentId = new ResourceLocation("block/composter_contents_ready"); + } else { + contentId = new ResourceLocation("block/composter_contents" + level); + } + builder.part(contentId).setCondition(state -> state.getValue(LEVEL).equals(level)).add(); + } + }); + builder.part(modelId).add(); + + return builder.build(); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseCraftingTableBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseCraftingTableBlock.java new file mode 100644 index 00000000..787d7ac7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseCraftingTableBlock.java @@ -0,0 +1,68 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.CraftingTableBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseCraftingTableBlock extends CraftingTableBlock implements BlockModelProvider { + public BaseCraftingTableBlock(Block source) { + this(FabricBlockSettings.copyOf(source)); + } + + public BaseCraftingTableBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this.asItem())); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String blockName = blockId.getPath(); + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_SIDED, new HashMap() { + private static final long serialVersionUID = 1L; + + { + put("%modid%", blockId.getNamespace()); + put("%particle%", blockName + "_front"); + put("%down%", blockName + "_bottom"); + put("%up%", blockName + "_top"); + put("%north%", blockName + "_front"); + put("%south%", blockName + "_side"); + put("%west%", blockName + "_front"); + put("%east%", blockName + "_side"); + } + }); + return ModelsHelper.fromPattern(pattern); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseCropBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseCropBlock.java new file mode 100644 index 00000000..fb4f2239 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseCropBlock.java @@ -0,0 +1,124 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +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.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.util.BlocksHelper; +import org.betterx.bclib.util.MHelper; + +import java.util.Collections; +import java.util.List; + +public class BaseCropBlock extends BasePlantBlock { + public static final IntegerProperty AGE = IntegerProperty.create("age", 0, 3); + private static final VoxelShape SHAPE = box(2, 0, 2, 14, 14, 14); + + private final Block[] terrain; + private final Item drop; + + public BaseCropBlock(Item drop, Block... terrain) { + this( + FabricBlockSettings.of(Material.PLANT) + .sound(SoundType.GRASS) + .randomTicks() + .noCollission() + .offsetType(BlockBehaviour.OffsetType.NONE), + drop, terrain + ); + } + + public BaseCropBlock(BlockBehaviour.Properties properties, Item drop, Block... terrain) { + super(properties.offsetType(BlockBehaviour.OffsetType.NONE)); + this.drop = drop; + this.terrain = terrain; + this.registerDefaultState(defaultBlockState().setValue(AGE, 0)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(AGE); + } + + @Override + protected boolean isTerrain(BlockState state) { + for (Block block : terrain) { + if (state.is(block)) { + return true; + } + } + return false; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + if (state.getValue(AGE) < 3) { + return Collections.singletonList(new ItemStack(this)); + } + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.isCorrectToolForDrops(state)) { + int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); + if (enchantment > 0) { + int countSeeds = MHelper.randRange(Mth.clamp(1 + enchantment, 1, 3), 3, MHelper.RANDOM_SOURCE); + int countDrops = MHelper.randRange(Mth.clamp(1 + enchantment, 1, 2), 2, MHelper.RANDOM_SOURCE); + return Lists.newArrayList(new ItemStack(this, countSeeds), new ItemStack(drop, countDrops)); + } + } + int countSeeds = MHelper.randRange(1, 3, MHelper.RANDOM_SOURCE); + int countDrops = MHelper.randRange(1, 2, MHelper.RANDOM_SOURCE); + return Lists.newArrayList(new ItemStack(this, countSeeds), new ItemStack(drop, countDrops)); + } + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + int age = state.getValue(AGE); + if (age < 3) { + BlocksHelper.setWithUpdate(level, pos, state.setValue(AGE, age + 1)); + } + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return state.getValue(AGE) < 3; + } + + @Override + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + return state.getValue(AGE) < 3; + } + + @Override + @SuppressWarnings("deprecation") + public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + super.tick(state, world, pos, random); + if (isBonemealSuccess(world, random, pos, state) && random.nextInt(8) == 0) { + performBonemeal(world, random, pos, state); + } + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseDoorBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseDoorBlock.java new file mode 100644 index 00000000..6a12373d --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseDoorBlock.java @@ -0,0 +1,181 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.DoorBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.DoorHingeSide; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.api.tag.NamedBlockTags; +import org.betterx.bclib.api.tag.NamedItemTags; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.interfaces.TagProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseDoorBlock extends DoorBlock implements RenderLayerProvider, BlockModelProvider, TagProvider { + public BaseDoorBlock(Block source) { + this(FabricBlockSettings.copyOf(source).strength(3F, 3F).noOcclusion()); + } + + public BaseDoorBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + if (state.getValue(HALF) == DoubleBlockHalf.LOWER) + return Collections.singletonList(new ItemStack(this.asItem())); + else return Collections.emptyList(); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + DoorType doorType = getDoorType(blockState); + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_BOTTOM, resourceLocation); + switch (doorType) { + case TOP_HINGE: + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_TOP_HINGE, resourceLocation); + break; + case BOTTOM_HINGE: + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_BOTTOM_HINGE, resourceLocation); + break; + case TOP: + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_TOP, resourceLocation); + break; + default: + break; + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + Direction facing = blockState.getValue(FACING); + DoorType doorType = getDoorType(blockState); + boolean open = blockState.getValue(OPEN); + boolean hinge = doorType.isHinge(); + BlockModelRotation rotation = BlockModelRotation.X0_Y0; + switch (facing) { + case EAST: + if (hinge && open) { + rotation = BlockModelRotation.X0_Y90; + } else if (open) { + rotation = BlockModelRotation.X0_Y270; + } + break; + case SOUTH: + if (!hinge && !open || hinge && !open) { + rotation = BlockModelRotation.X0_Y90; + } else if (hinge) { + rotation = BlockModelRotation.X0_Y180; + } + break; + case WEST: + if (!hinge && !open || hinge && !open) { + rotation = BlockModelRotation.X0_Y180; + } else if (hinge) { + rotation = BlockModelRotation.X0_Y270; + } else { + rotation = BlockModelRotation.X0_Y90; + } + break; + case NORTH: + default: + if (!hinge && !open || hinge && !open) { + rotation = BlockModelRotation.X0_Y270; + } else if (!hinge) { + rotation = BlockModelRotation.X0_Y180; + } + break; + } + ResourceLocation modelId = new ResourceLocation( + stateId.getNamespace(), + "block/" + stateId.getPath() + "_" + doorType + ); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } + + protected DoorType getDoorType(BlockState blockState) { + boolean isHinge = isHinge(blockState.getValue(HINGE), blockState.getValue(OPEN)); + switch (blockState.getValue(HALF)) { + case UPPER: { + return isHinge ? DoorType.TOP_HINGE : DoorType.TOP; + } + case LOWER: { + return isHinge ? DoorType.BOTTOM_HINGE : DoorType.BOTTOM; + } + } + return DoorType.BOTTOM; + } + + private boolean isHinge(DoorHingeSide hingeSide, boolean open) { + boolean isHinge = hingeSide == DoorHingeSide.RIGHT; + return isHinge && !open || !isHinge && open; + } + + @Override + public void addTags(List> blockTags, List> itemTags) { + blockTags.add(NamedBlockTags.DOORS); + itemTags.add(NamedItemTags.DOORS); + } + + protected enum DoorType implements StringRepresentable { + BOTTOM_HINGE("bottom_hinge"), TOP_HINGE("top_hinge"), BOTTOM("bottom"), TOP("top"); + + private final String name; + + DoorType(String name) { + this.name = name; + } + + public boolean isHinge() { + return this == BOTTOM_HINGE || this == TOP_HINGE; + } + + @Override + public String toString() { + return getSerializedName(); + } + + @Override + public String getSerializedName() { + return name; + } + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseDoublePlantBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseDoublePlantBlock.java new file mode 100644 index 00000000..9b2c0dad --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseDoublePlantBlock.java @@ -0,0 +1,166 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +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.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealableBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.BlocksHelper; + +import java.util.List; + +public abstract class BaseDoublePlantBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock { + private static final VoxelShape SHAPE = box(4, 2, 4, 12, 16, 12); + public static final IntegerProperty ROTATION = BlockProperties.ROTATION; + public static final BooleanProperty TOP = BooleanProperty.create("top"); + + public BaseDoublePlantBlock() { + this( + FabricBlockSettings.of(Material.PLANT) + .sound(SoundType.GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ) + ); + } + + public BaseDoublePlantBlock(int light) { + this( + FabricBlockSettings.of(Material.PLANT) + .sound(SoundType.GRASS) + .lightLevel((state) -> state.getValue(TOP) ? light : 0) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ) + ); + } + + public BaseDoublePlantBlock(BlockBehaviour.Properties properties) { + super(properties); + this.registerDefaultState(this.stateDefinition.any().setValue(TOP, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(TOP, ROTATION); + } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + @SuppressWarnings("deprecation") + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + BlockState up = world.getBlockState(pos.above()); + return state.getValue(TOP) ? down.getBlock() == this : isTerrain(down) && (up.getMaterial().isReplaceable()); + } + + public boolean canStayAt(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + BlockState up = world.getBlockState(pos.above()); + return state.getValue(TOP) ? down.getBlock() == this : isTerrain(down) && (up.getBlock() == this); + } + + protected abstract boolean isTerrain(BlockState state); + + @Override + @SuppressWarnings("deprecation") + public BlockState updateShape(BlockState state, + Direction facing, + BlockState neighborState, + LevelAccessor world, + BlockPos pos, + BlockPos neighborPos) { + if (!canStayAt(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } else { + return state; + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + if (state.getValue(TOP)) { + return Lists.newArrayList(); + } + + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + //TODO: 1.18.2 Test if shearing still works + if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( + Enchantments.SILK_TOUCH, + tool + ) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } else { + return Lists.newArrayList(); + } + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return true; + } + + @Override + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + ItemEntity item = new ItemEntity( + level, + pos.getX() + 0.5, + pos.getY() + 0.5, + pos.getZ() + 0.5, + new ItemStack(this) + ); + level.addFreshEntity(item); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + int rot = world.random.nextInt(4); + BlockState bs = this.defaultBlockState().setValue(ROTATION, rot); + BlocksHelper.setWithoutUpdate(world, pos, bs); + BlocksHelper.setWithoutUpdate(world, pos.above(), bs.setValue(TOP, true)); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseFenceBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseFenceBlock.java new file mode 100644 index 00000000..e0f21ccd --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseFenceBlock.java @@ -0,0 +1,97 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FenceBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseFenceBlock extends FenceBlock implements BlockModelProvider { + private final Block parent; + + public BaseFenceBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + this.parent = source; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_FENCE, parentId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + String path = blockId.getPath(); + Optional pattern = Optional.empty(); + if (path.endsWith("_post")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FENCE_POST, parentId); + } + if (path.endsWith("_side")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FENCE_SIDE, parentId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_post"); + ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_side"); + registerBlockModel(postId, postId, blockState, modelCache); + registerBlockModel(sideId, sideId, blockState, modelCache); + + ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); + builder.part(sideId).setCondition(state -> state.getValue(NORTH)).setUVLock(true).add(); + builder.part(sideId) + .setCondition(state -> state.getValue(EAST)) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideId) + .setCondition(state -> state.getValue(SOUTH)) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideId) + .setCondition(state -> state.getValue(WEST)) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()) + .setUVLock(true) + .add(); + builder.part(postId).add(); + + return builder.build(); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseFurnaceBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseFurnaceBlock.java new file mode 100644 index 00000000..9cefcba7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseFurnaceBlock.java @@ -0,0 +1,140 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.stats.Stats; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FurnaceBlock; +import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.blockentities.BaseFurnaceBlockEntity; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.registry.BaseBlockEntities; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseFurnaceBlock extends FurnaceBlock implements BlockModelProvider, RenderLayerProvider { + public BaseFurnaceBlock(Block source) { + this(FabricBlockSettings.copyOf(source).lightLevel(state -> state.getValue(LIT) ? 13 : 0)); + } + + public BaseFurnaceBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new BaseFurnaceBlockEntity(blockPos, blockState); + } + + @Override + protected void openContainer(Level world, BlockPos pos, Player player) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof BaseFurnaceBlockEntity) { + player.openMenu((MenuProvider) blockEntity); + player.awardStat(Stats.INTERACT_WITH_FURNACE); + } + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String blockName = blockId.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%modid%", blockId.getNamespace()); + textures.put("%top%", blockName + "_top"); + textures.put("%side%", blockName + "_side"); + Optional pattern; + if (blockState.getValue(LIT)) { + textures.put("%front%", blockName + "_front_on"); + textures.put("%glow%", blockName + "_glow"); + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FURNACE_LIT, textures); + } else { + textures.put("%front%", blockName + "_front"); + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FURNACE, textures); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + String lit = blockState.getValue(LIT) ? "_lit" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + lit); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), false, true); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + List drop = Lists.newArrayList(new ItemStack(this)); + BlockEntity blockEntity = builder.getOptionalParameter(LootContextParams.BLOCK_ENTITY); + if (blockEntity instanceof BaseFurnaceBlockEntity) { + BaseFurnaceBlockEntity entity = (BaseFurnaceBlockEntity) blockEntity; + for (int i = 0; i < entity.getContainerSize(); i++) { + drop.add(entity.getItem(i)); + } + } + return drop; + } + + @Override + @Nullable + public BlockEntityTicker getTicker(Level level, + BlockState blockState, + BlockEntityType blockEntityType) { + return createFurnaceTicker(level, blockEntityType, BaseBlockEntities.FURNACE); + } + + @Nullable + protected static BlockEntityTicker createFurnaceTicker(Level level, + BlockEntityType blockEntityType, + BlockEntityType blockEntityType2) { + return level.isClientSide ? null : createTickerHelper( + blockEntityType, + blockEntityType2, + AbstractFurnaceBlockEntity::serverTick + ); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseGateBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseGateBlock.java new file mode 100644 index 00000000..f1a1c22c --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseGateBlock.java @@ -0,0 +1,85 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FenceGateBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseGateBlock extends FenceGateBlock implements BlockModelProvider { + private final Block parent; + + public BaseGateBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + this.parent = source; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + boolean inWall = blockState.getValue(IN_WALL); + boolean isOpen = blockState.getValue(OPEN); + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (inWall) { + pattern = isOpen + ? PatternsHelper.createJson( + BasePatterns.BLOCK_GATE_OPEN_WALL, + parentId + ) + : PatternsHelper.createJson(BasePatterns.BLOCK_GATE_CLOSED_WALL, parentId); + } else { + pattern = isOpen + ? PatternsHelper.createJson( + BasePatterns.BLOCK_GATE_OPEN, + parentId + ) + : PatternsHelper.createJson(BasePatterns.BLOCK_GATE_CLOSED, parentId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + boolean inWall = blockState.getValue(IN_WALL); + boolean isOpen = blockState.getValue(OPEN); + String state = "" + (inWall ? "_wall" : "") + (isOpen ? "_open" : "_closed"); + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), true, false); + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/BaseGlassBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseGlassBlock.java similarity index 77% rename from src/main/java/ru/bclib/blocks/BaseGlassBlock.java rename to src/main/java/org/betterx/bclib/blocks/BaseGlassBlock.java index a7766e36..07cfa128 100644 --- a/src/main/java/ru/bclib/blocks/BaseGlassBlock.java +++ b/src/main/java/org/betterx/bclib/blocks/BaseGlassBlock.java @@ -1,8 +1,5 @@ -package ru.bclib.blocks; +package org.betterx.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.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; @@ -13,9 +10,14 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.interfaces.tools.AddMineablePickaxe; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.interfaces.tools.AddMineablePickaxe; import java.util.Collections; import java.util.List; @@ -24,12 +26,13 @@ public class BaseGlassBlock extends BaseBlockNotFull implements AddMineablePicka public BaseGlassBlock(Block block) { this(block, 0.3f); } + public BaseGlassBlock(Block block, float resistance) { super(FabricBlockSettings.copyOf(block) - .resistance(resistance) - .nonOpaque() - .isSuffocating((arg1, arg2, arg3) -> false) - .isViewBlocking((arg1, arg2, arg3) -> false)); + .resistance(resistance) + .nonOpaque() + .isSuffocating((arg1, arg2, arg3) -> false) + .isViewBlocking((arg1, arg2, arg3) -> false)); } @Environment(EnvType.CLIENT) @@ -44,7 +47,7 @@ public class BaseGlassBlock extends BaseBlockNotFull implements AddMineablePicka @Environment(EnvType.CLIENT) public boolean skipRendering(BlockState state, BlockState neighbor, Direction facing) { - return neighbor.getBlock() == this ? true : super.skipRendering(state, neighbor, facing); + return neighbor.getBlock() == this || super.skipRendering(state, neighbor, facing); } @Override diff --git a/src/main/java/org/betterx/bclib/blocks/BaseLadderBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseLadderBlock.java new file mode 100644 index 00000000..1f0ec265 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseLadderBlock.java @@ -0,0 +1,72 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.LadderBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseLadderBlock extends LadderBlock implements RenderLayerProvider, BlockModelProvider { + public BaseLadderBlock(Block block) { + this(FabricBlockSettings.copyOf(block).noOcclusion()); + } + + public BaseLadderBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return ModelsHelper.createBlockItem(blockId); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_LADDER, blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), false, true); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseLeavesBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseLeavesBlock.java new file mode 100644 index 00000000..1280ed52 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseLeavesBlock.java @@ -0,0 +1,118 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +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.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LeavesBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.api.tag.NamedBlockTags; +import org.betterx.bclib.api.tag.NamedItemTags; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.interfaces.TagProvider; +import org.betterx.bclib.interfaces.tools.AddMineableHoe; +import org.betterx.bclib.interfaces.tools.AddMineableShears; +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MHelper; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +public class BaseLeavesBlock extends LeavesBlock implements BlockModelProvider, RenderLayerProvider, TagProvider, AddMineableShears, AddMineableHoe { + protected final Block sapling; + + private static FabricBlockSettings makeLeaves(MaterialColor color) { + return FabricBlockSettings + .copyOf(Blocks.OAK_LEAVES) + .mapColor(color) + //.requiresTool() + .allowsSpawning((state, world, pos, type) -> false) + .suffocates((state, world, pos) -> false) + .blockVision((state, world, pos) -> false); + } + + public BaseLeavesBlock(Block sapling, MaterialColor color, Consumer customizeProperties) { + super(BaseBlock.acceptAndReturn(customizeProperties, makeLeaves(color))); + this.sapling = sapling; + } + + public BaseLeavesBlock(Block sapling, + MaterialColor color, + int light, + Consumer customizeProperties) { + super(BaseBlock.acceptAndReturn(customizeProperties, makeLeaves(color).luminance(light))); + this.sapling = sapling; + } + + public BaseLeavesBlock(Block sapling, MaterialColor color) { + super(makeLeaves(color)); + this.sapling = sapling; + } + + public BaseLeavesBlock(Block sapling, MaterialColor color, int light) { + super(makeLeaves(color).lightLevel(light)); + this.sapling = sapling; + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return BaseLeavesBlock.getLeaveDrops(this, this.sapling, builder, 16, 16); + } + + public static List getLeaveDrops(ItemLike leaveBlock, + Block sapling, + LootContext.Builder builder, + int fortuneRate, + int dropRate) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null) { + if (BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( + Enchantments.SILK_TOUCH, + tool + ) > 0) { + return Collections.singletonList(new ItemStack(leaveBlock)); + } + int fortune = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); + if (MHelper.RANDOM.nextInt(fortuneRate) <= fortune) { + return Lists.newArrayList(new ItemStack(sapling)); + } + return Lists.newArrayList(); + } + return MHelper.RANDOM.nextInt(dropRate) == 0 + ? Lists.newArrayList(new ItemStack(sapling)) + : Lists.newArrayList(); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public void addTags(List> blockTags, List> itemTags) { + blockTags.add(NamedBlockTags.LEAVES); + itemTags.add(NamedItemTags.LEAVES); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseMetalBarsBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseMetalBarsBlock.java new file mode 100644 index 00000000..6990cbc9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseMetalBarsBlock.java @@ -0,0 +1,128 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.IronBarsBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseMetalBarsBlock extends IronBarsBlock implements BlockModelProvider, RenderLayerProvider { + public BaseMetalBarsBlock(Block source) { + this(FabricBlockSettings.copyOf(source).strength(5.0F, 6.0F).noOcclusion()); + } + + public BaseMetalBarsBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + public Optional getModelString(String block) { + ResourceLocation blockId = Registry.BLOCK.getKey(this); + if (block.contains("item")) { + return PatternsHelper.createJson(BasePatterns.ITEM_BLOCK, blockId); + } + if (block.contains("post")) { + return PatternsHelper.createJson(BasePatterns.BLOCK_BARS_POST, blockId); + } else { + return PatternsHelper.createJson(BasePatterns.BLOCK_BARS_SIDE, blockId); + } + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createBlockItem(resourceLocation); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation thisId = Registry.BLOCK.getKey(this); + String path = blockId.getPath(); + Optional pattern = Optional.empty(); + if (path.endsWith("_post")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BARS_POST, thisId); + } + if (path.endsWith("_side")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BARS_SIDE, thisId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_post"); + ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_side"); + registerBlockModel(postId, postId, blockState, modelCache); + registerBlockModel(sideId, sideId, blockState, modelCache); + + ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); + builder.part(postId) + .setCondition(state -> !state.getValue(NORTH) && !state.getValue(EAST) && !state.getValue(SOUTH) && !state + .getValue(WEST)) + .add(); + builder.part(sideId).setCondition(state -> state.getValue(NORTH)).setUVLock(true).add(); + builder.part(sideId) + .setCondition(state -> state.getValue(EAST)) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideId) + .setCondition(state -> state.getValue(SOUTH)) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideId) + .setCondition(state -> state.getValue(WEST)) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()) + .setUVLock(true) + .add(); + + return builder.build(); + } + + @Environment(EnvType.CLIENT) + public boolean skipRendering(BlockState state, BlockState stateFrom, Direction direction) { + if (direction.getAxis().isVertical() && stateFrom.getBlock() == this && !stateFrom.equals(state)) { + return false; + } + return super.skipRendering(state, stateFrom, direction); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseOreBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseOreBlock.java new file mode 100644 index 00000000..d225ce74 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseOreBlock.java @@ -0,0 +1,125 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TieredItem; +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.DropExperienceBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.util.LootUtil; +import org.betterx.bclib.util.MHelper; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +public class BaseOreBlock extends DropExperienceBlock implements BlockModelProvider { + private final Supplier dropItem; + private final int minCount; + private final int maxCount; + private final int miningLevel; + + public BaseOreBlock(Supplier drop, int minCount, int maxCount, int experience) { + this(drop, minCount, maxCount, experience, 0); + } + + public BaseOreBlock(Supplier drop, int minCount, int maxCount, int experience, int miningLevel) { + this( + FabricBlockSettings + .of(Material.STONE, MaterialColor.SAND) + .requiresTool() + .destroyTime(3F) + .explosionResistance(9F) + .sound(SoundType.STONE), + drop, minCount, maxCount, experience, miningLevel + ); + } + + public BaseOreBlock(Properties properties, Supplier drop, int minCount, int maxCount, int experience) { + this(properties, drop, minCount, maxCount, experience, 0); + } + + public BaseOreBlock(Properties properties, + Supplier drop, + int minCount, + int maxCount, + int experience, + int miningLevel) { + super(properties, UniformInt.of(experience > 0 ? 1 : 0, experience)); + this.dropItem = drop; + this.minCount = minCount; + this.maxCount = maxCount; + this.miningLevel = miningLevel; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return LootUtil + .getDrops(this, state, builder) + .orElseGet( + () -> BaseOreBlock.getDroppedItems(this, + dropItem.get(), + maxCount, + minCount, + miningLevel, + state, + builder) + ); + } + + public static List getDroppedItems(ItemLike block, + Item dropItem, + int maxCount, + int minCount, + int miningLevel, + BlockState state, + LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.isCorrectToolForDrops(state)) { + boolean canMine = miningLevel == 0; + if (tool.getItem() instanceof TieredItem tired) { + canMine = tired.getTier().getLevel() >= miningLevel; + } + if (canMine) { + if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(block)); + } + int count; + int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); + if (enchantment > 0) { + int min = Mth.clamp(minCount + enchantment, minCount, maxCount); + int max = maxCount + (enchantment / Enchantments.BLOCK_FORTUNE.getMaxLevel()); + if (min == max) { + return Collections.singletonList(new ItemStack(dropItem, max)); + } + count = MHelper.randRange(min, max, MHelper.RANDOM_SOURCE); + } else { + count = MHelper.randRange(minCount, maxCount, MHelper.RANDOM_SOURCE); + } + return Collections.singletonList(new ItemStack(dropItem, count)); + } + } + return Collections.emptyList(); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BasePathBlock.java b/src/main/java/org/betterx/bclib/blocks/BasePathBlock.java new file mode 100644 index 00000000..bc734313 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BasePathBlock.java @@ -0,0 +1,101 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +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.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Maps; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BasePathBlock extends BaseBlockNotFull { + private static final VoxelShape SHAPE = box(0, 0, 0, 16, 15, 16); + + private Block baseBlock; + + public BasePathBlock(Block source) { + super(FabricBlockSettings.copyOf(source).isValidSpawn((state, world, pos, type) -> false)); + this.baseBlock = Blocks.DIRT; + if (source instanceof BaseTerrainBlock) { + BaseTerrainBlock terrain = (BaseTerrainBlock) source; + this.baseBlock = terrain.getBaseBlock(); + terrain.setPathBlock(this); + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(this)); + } + return Collections.singletonList(new ItemStack(Blocks.END_STONE)); + } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getCollisionShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String name = blockId.getPath(); + ResourceLocation bottomId = Registry.BLOCK.getKey(baseBlock); + String bottom = bottomId.getNamespace() + ":block/" + bottomId.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%modid%", blockId.getNamespace()); + textures.put("%top%", name + "_top"); + textures.put("%side%", name.replace("_path", "") + "_side"); + textures.put("%bottom%", bottom); + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PATH, textures); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRandomTopModel(modelId); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BasePlantBlock.java b/src/main/java/org/betterx/bclib/blocks/BasePlantBlock.java new file mode 100644 index 00000000..f7de63cd --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BasePlantBlock.java @@ -0,0 +1,181 @@ +package org.betterx.bclib.blocks; + +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.util.RandomSource; +import net.minecraft.world.entity.item.ItemEntity; +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.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealableBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.items.tool.BaseShearsItem; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + +public abstract class BasePlantBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock { + private static final VoxelShape SHAPE = box(4, 0, 4, 12, 14, 12); + + public BasePlantBlock() { + this(false, p -> p); + } + + public BasePlantBlock(int light) { + this(light, p -> p); + } + + public BasePlantBlock(int light, Function propMod) { + this(false, light, propMod); + } + + public BasePlantBlock(boolean replaceabled) { + this(replaceabled, p -> p); + } + + public BasePlantBlock(boolean replaceable, Function propMod) { + this( + propMod.apply(FabricBlockSettings + .of(replaceable ? Material.REPLACEABLE_PLANT : Material.PLANT) + .sound(SoundType.GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ) + ) + ); + } + + public BasePlantBlock(boolean replaceable, int light) { + this(replaceable, light, p -> p); + } + + public BasePlantBlock(boolean replaceable, int light, Function propMod) { + this( + propMod.apply(FabricBlockSettings + .of(replaceable ? Material.REPLACEABLE_PLANT : Material.PLANT) + .luminance(light) + .sound(SoundType.GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ) + ) + ); + } + + public BasePlantBlock(Properties settings) { + super(settings.offsetType(BlockBehaviour.OffsetType.XZ)); + } + + protected abstract boolean isTerrain(BlockState state); + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + @SuppressWarnings("deprecation") + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + return isTerrain(down); + } + + @Override + @SuppressWarnings("deprecation") + 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 List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + //TODO: 1.18.2 Test if shearing still works + if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( + Enchantments.SILK_TOUCH, + tool + ) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } else { + return Lists.newArrayList(); + } + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return true; + } + + @Override + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + ItemEntity item = new ItemEntity( + level, + pos.getX() + 0.5, + pos.getY() + 0.5, + pos.getZ() + 0.5, + new ItemStack(this) + ); + level.addFreshEntity(item); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createBlockItem(resourceLocation); + } + + @Override + @Nullable + @Environment(EnvType.CLIENT) + public BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS, resourceLocation); + return ModelsHelper.fromPattern(pattern); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BasePlantWithAgeBlock.java b/src/main/java/org/betterx/bclib/blocks/BasePlantWithAgeBlock.java new file mode 100644 index 00000000..daf27f00 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BasePlantWithAgeBlock.java @@ -0,0 +1,69 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import java.util.function.Function; + +public abstract class BasePlantWithAgeBlock extends BasePlantBlock { + public static final IntegerProperty AGE = BlockProperties.AGE; + + public BasePlantWithAgeBlock() { + this(p -> p); + } + + public BasePlantWithAgeBlock(Function propMod) { + this( + propMod.apply(FabricBlockSettings.of(Material.PLANT) + .sound(SoundType.GRASS) + .randomTicks() + .noCollission()) + ); + } + + public BasePlantWithAgeBlock(Properties settings) { + super(settings); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(AGE); + } + + public abstract void growAdult(WorldGenLevel world, RandomSource random, BlockPos pos); + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + int age = state.getValue(AGE); + if (age < 3) { + level.setBlockAndUpdate(pos, state.setValue(AGE, age + 1)); + } else { + growAdult(level, random, pos); + } + } + + @Override + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + super.tick(state, world, pos, random); + if (random.nextInt(8) == 0) { + performBonemeal(world, random, pos, state); + } + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BasePressurePlateBlock.java b/src/main/java/org/betterx/bclib/blocks/BasePressurePlateBlock.java new file mode 100644 index 00000000..34047308 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BasePressurePlateBlock.java @@ -0,0 +1,71 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.PressurePlateBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BasePressurePlateBlock extends PressurePlateBlock implements BlockModelProvider { + private final Block parent; + + public BasePressurePlateBlock(Sensitivity rule, Block source) { + super(rule, FabricBlockSettings.copyOf(source).noCollission().noOcclusion().strength(0.5F)); + this.parent = source; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (blockState.getValue(POWERED)) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_DOWN, parentId); + } else { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_UP, parentId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + String state = blockState.getValue(POWERED) ? "_down" : "_up"; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createBlockSimple(modelId); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseRotatedPillarBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseRotatedPillarBlock.java new file mode 100644 index 00000000..4a7a35e4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseRotatedPillarBlock.java @@ -0,0 +1,67 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RotatedPillarBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseRotatedPillarBlock extends RotatedPillarBlock implements BlockModelProvider { + public BaseRotatedPillarBlock(Properties settings) { + super(settings); + } + + public BaseRotatedPillarBlock(Block block) { + this(FabricBlockSettings.copyOf(block)); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = createBlockPattern(blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRotatedModel(modelId, blockState.getValue(AXIS)); + } + + protected Optional createBlockPattern(ResourceLocation blockId) { + return PatternsHelper.createBlockPillar(blockId); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseSignBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseSignBlock.java new file mode 100644 index 00000000..72f7f547 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseSignBlock.java @@ -0,0 +1,198 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.block.state.properties.WoodType; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.blockentities.BaseSignBlockEntity; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.CustomItemProvider; +import org.betterx.bclib.util.BlocksHelper; + +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("deprecation") +public class BaseSignBlock extends SignBlock implements BlockModelProvider, CustomItemProvider { + public static final IntegerProperty ROTATION = BlockStateProperties.ROTATION_16; + public static final BooleanProperty FLOOR = BooleanProperty.create("floor"); + private static final VoxelShape[] WALL_SHAPES = new VoxelShape[]{ + Block.box(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D), + Block.box(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D), + Block.box(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D), + Block.box(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D) + }; + + private final Block parent; + + public BaseSignBlock(Block source) { + super(FabricBlockSettings.copyOf(source).strength(1.0F, 1.0F).noCollission().noOcclusion(), WoodType.OAK); + this.registerDefaultState(this.stateDefinition.any() + .setValue(ROTATION, 0) + .setValue(FLOOR, false) + .setValue(WATERLOGGED, false)); + this.parent = source; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(ROTATION, FLOOR, WATERLOGGED); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return state.getValue(FLOOR) ? SHAPE : WALL_SHAPES[state.getValue(ROTATION) >> 2]; + } + + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new BaseSignBlockEntity(blockPos, blockState); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + if (placer instanceof Player) { + BaseSignBlockEntity sign = (BaseSignBlockEntity) world.getBlockEntity(pos); + if (sign != null) { + if (!world.isClientSide) { + sign.setAllowedPlayerEditor(placer.getUUID()); + ((ServerPlayer) placer).connection.send(new ClientboundOpenSignEditorPacket(pos)); + } else { + sign.setEditable(true); + } + } + } + } + + @Override + public BlockState updateShape(BlockState state, + Direction facing, + BlockState neighborState, + LevelAccessor world, + BlockPos pos, + BlockPos neighborPos) { + if (state.getValue(WATERLOGGED)) { + world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); + } + if (!canSurvive(state, world, pos)) { + return state.getValue(WATERLOGGED) + ? state.getFluidState().createLegacyBlock() + : Blocks.AIR.defaultBlockState(); + } + return super.updateShape(state, facing, neighborState, world, pos, neighborPos); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + if (!state.getValue(FLOOR)) { + int index = (((state.getValue(ROTATION) >> 2) + 2)) & 3; + return world.getBlockState(pos.relative(BlocksHelper.HORIZONTAL[index])).getMaterial().isSolid(); + } else { + return world.getBlockState(pos.below()).getMaterial().isSolid(); + } + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + if (ctx.getClickedFace() == Direction.UP) { + FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); + return this + .defaultBlockState() + .setValue(FLOOR, true) + .setValue(ROTATION, Mth.floor((180.0 + ctx.getRotation() * 16.0 / 360.0) + 0.5 - 12) & 15) + .setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); + } else if (ctx.getClickedFace() != Direction.DOWN) { + BlockState blockState = this.defaultBlockState(); + FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + Direction[] directions = ctx.getNearestLookingDirections(); + + for (Direction direction : directions) { + if (direction.getAxis().isHorizontal()) { + Direction dir = direction.getOpposite(); + int rot = Mth.floor((180.0 + dir.toYRot() * 16.0 / 360.0) + 0.5 + 4) & 15; + blockState = blockState.setValue(ROTATION, rot); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState.setValue(FLOOR, false) + .setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); + } + } + } + } + + return null; + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + return ModelsHelper.createBlockEmpty(parentId); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(ROTATION, rotation.rotate(state.getValue(ROTATION), 16)); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.setValue(ROTATION, mirror.mirror(state.getValue(ROTATION), 16)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return super.canPlaceLiquid(world, pos, state, fluid); + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return super.placeLiquid(world, pos, state, fluidState); + } + + @Override + public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { + return new BlockItem(this, settings.stacksTo(16)); + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/blocks/BaseSlabBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseSlabBlock.java new file mode 100644 index 00000000..10517af5 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseSlabBlock.java @@ -0,0 +1,95 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SlabBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.CustomItemProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseSlabBlock extends SlabBlock implements BlockModelProvider, CustomItemProvider { + private final Block parent; + public final boolean fireproof; + + public BaseSlabBlock(Block source) { + this(source, false); + } + + public BaseSlabBlock(Block source, boolean fireproof) { + super(FabricBlockSettings.copyOf(source)); + this.parent = source; + this.fireproof = fireproof; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + int count = state.getValue(TYPE) == SlabType.DOUBLE ? 2 : 1; + return Collections.singletonList(new ItemStack(this, count)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (blockState.getValue(TYPE) == SlabType.DOUBLE) { + pattern = PatternsHelper.createBlockSimple(parentId); + } else { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_SLAB, parentId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + SlabType type = blockState.getValue(TYPE); + ResourceLocation modelId = new ResourceLocation( + stateId.getNamespace(), + "block/" + stateId.getPath() + "_" + type + ); + registerBlockModel(stateId, modelId, blockState, modelCache); + if (type == SlabType.TOP) { + return ModelsHelper.createMultiVariant(modelId, BlockModelRotation.X180_Y0.getRotation(), true); + } + return ModelsHelper.createBlockSimple(modelId); + } + + @Override + public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { + if (fireproof) settings = settings.fireproof(); + return new BlockItem(this, settings); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseStairsBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseStairsBlock.java new file mode 100644 index 00000000..fe0215ff --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseStairsBlock.java @@ -0,0 +1,119 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.StairBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Half; +import net.minecraft.world.level.block.state.properties.StairsShape; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.CustomItemProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseStairsBlock extends StairBlock implements BlockModelProvider, CustomItemProvider { + private final Block parent; + public final boolean fireproof; + + public BaseStairsBlock(Block source) { + this(source, false); + } + + public BaseStairsBlock(Block source, boolean fireproof) { + super(source.defaultBlockState(), FabricBlockSettings.copyOf(source)); + this.parent = source; + this.fireproof = fireproof; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = PatternsHelper.createJson(switch (blockState.getValue(SHAPE)) { + case STRAIGHT -> BasePatterns.BLOCK_STAIR; + case INNER_LEFT, INNER_RIGHT -> BasePatterns.BLOCK_STAIR_INNER; + case OUTER_LEFT, OUTER_RIGHT -> BasePatterns.BLOCK_STAIR_OUTER; + }, parentId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + String state; + StairsShape shape = blockState.getValue(SHAPE); + state = switch (shape) { + case INNER_LEFT, INNER_RIGHT -> "_inner"; + case OUTER_LEFT, OUTER_RIGHT -> "_outer"; + default -> ""; + }; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + + boolean isTop = blockState.getValue(HALF) == Half.TOP; + boolean isLeft = shape == StairsShape.INNER_LEFT || shape == StairsShape.OUTER_LEFT; + boolean isRight = shape == StairsShape.INNER_RIGHT || shape == StairsShape.OUTER_RIGHT; + int y = 0; + int x = isTop ? 180 : 0; + switch (blockState.getValue(FACING)) { + case NORTH: + if (isTop && !isRight) y = 270; + else if (!isTop) y = isLeft ? 180 : 270; + break; + case EAST: + if (isTop && isRight) y = 90; + else if (!isTop && isLeft) y = 270; + break; + case SOUTH: + if (isTop) y = isRight ? 180 : 90; + else if (!isLeft) y = 90; + break; + case WEST: + default: + y = (isTop && isRight) ? 270 : (!isTop && isLeft) ? 90 : 180; + break; + } + BlockModelRotation rotation = BlockModelRotation.by(x, y); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), true); + } + + @Override + public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { + if (fireproof) settings = settings.fireproof(); + return new BlockItem(this, settings); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseStoneButtonBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseStoneButtonBlock.java new file mode 100644 index 00000000..c1ffc89e --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseStoneButtonBlock.java @@ -0,0 +1,18 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.Block; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +public class BaseStoneButtonBlock extends BaseButtonBlock { + public BaseStoneButtonBlock(Block source) { + super(source, FabricBlockSettings.copyOf(source).noOcclusion(), false); + } + + @Override + protected SoundEvent getSound(boolean clicked) { + return clicked ? SoundEvents.STONE_BUTTON_CLICK_ON : SoundEvents.STONE_BUTTON_CLICK_OFF; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseStripableLogBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseStripableLogBlock.java new file mode 100644 index 00000000..a5cd8ca4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseStripableLogBlock.java @@ -0,0 +1,54 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RotatedPillarBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.BlockHitResult; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.api.tag.NamedMineableTags; +import org.betterx.bclib.api.tag.TagAPI; + +public class BaseStripableLogBlock extends BaseRotatedPillarBlock { + private final Block striped; + + public BaseStripableLogBlock(MaterialColor color, Block striped) { + super(FabricBlockSettings.copyOf(striped).color(color)); + this.striped = striped; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, + Level world, + BlockPos pos, + Player player, + InteractionHand hand, + BlockHitResult hit) { + if (TagAPI.isToolWithMineableTag(player.getMainHandItem(), NamedMineableTags.AXE)) { + world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); + if (!world.isClientSide) { + world.setBlock(pos, + striped.defaultBlockState() + .setValue(RotatedPillarBlock.AXIS, state.getValue(RotatedPillarBlock.AXIS)), + 11 + ); + if (!player.isCreative()) { + player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); + } + } + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseTerrainBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseTerrainBlock.java new file mode 100644 index 00000000..12437273 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseTerrainBlock.java @@ -0,0 +1,162 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +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.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.SnowLayerBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.lighting.LayerLightEngine; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.BlockHitResult; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Maps; +import org.betterx.bclib.api.tag.NamedMineableTags; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.sound.BlockSounds; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("deprecation") +public class BaseTerrainBlock extends BaseBlock { + private final Block baseBlock; + private Block pathBlock; + + public BaseTerrainBlock(Block baseBlock, MaterialColor color) { + super(FabricBlockSettings + .copyOf(baseBlock) + .materialColor(color) + .sound(BlockSounds.TERRAIN_SOUND) + .randomTicks() + ); + this.baseBlock = baseBlock; + } + + public void setPathBlock(Block roadBlock) { + this.pathBlock = roadBlock; + } + + public Block getBaseBlock() { + return baseBlock; + } + + @Override + public InteractionResult use(BlockState state, + Level world, + BlockPos pos, + Player player, + InteractionHand hand, + BlockHitResult hit) { + //TODO: 1.18.2 check + if (pathBlock != null && TagAPI.isToolWithMineableTag(player.getMainHandItem(), NamedMineableTags.SHOVEL)) { + //if (pathBlock != null && FabricTagProvider.SHOVELS.contains(player.getMainHandItem().getItem())) { + world.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); + if (!world.isClientSide) { + world.setBlockAndUpdate(pos, pathBlock.defaultBlockState()); + if (!player.isCreative()) { + player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); + } + } + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(this)); + } + return Collections.singletonList(new ItemStack(getBaseBlock())); + } + + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + if (random.nextInt(16) == 0 && !canStay(state, world, pos)) { + world.setBlockAndUpdate(pos, getBaseBlock().defaultBlockState()); + } + } + + public boolean canStay(BlockState state, LevelReader worldView, BlockPos pos) { + BlockPos blockPos = pos.above(); + BlockState blockState = worldView.getBlockState(blockPos); + if (blockState.is(Blocks.SNOW) && blockState.getValue(SnowLayerBlock.LAYERS) == 1) { + return true; + } else if (blockState.getFluidState().getAmount() == 8) { + return false; + } else { + int i = LayerLightEngine.getLightBlockInto( + worldView, + state, + pos, + blockState, + blockPos, + Direction.UP, + blockState.getLightBlock(worldView, blockPos) + ); + return i < 5; + } + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation baseId = Registry.BLOCK.getKey(getBaseBlock()); + String modId = blockId.getNamespace(); + String path = blockId.getPath(); + String bottom = baseId.getNamespace() + ":block/" + baseId.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%top%", modId + ":block/" + path + "_top"); + textures.put("%side%", modId + ":block/" + path + "_side"); + textures.put("%bottom%", bottom); + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_TOP_SIDE_BOTTOM, textures); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRandomTopModel(modelId); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseTrapdoorBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseTrapdoorBlock.java new file mode 100644 index 00000000..7e367ecb --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseTrapdoorBlock.java @@ -0,0 +1,104 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.TrapDoorBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Half; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; + +import java.util.*; +import org.jetbrains.annotations.Nullable; + +public class BaseTrapdoorBlock extends TrapDoorBlock implements RenderLayerProvider, BlockModelProvider { + public BaseTrapdoorBlock(Block source) { + this(FabricBlockSettings.copyOf(source).strength(3.0F, 3.0F).noOcclusion()); + } + + public BaseTrapdoorBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + String name = resourceLocation.getPath(); + Optional pattern = PatternsHelper.createJson( + BasePatterns.BLOCK_TRAPDOOR, + new HashMap() { + private static final long serialVersionUID = 1L; + + { + put("%modid%", resourceLocation.getNamespace()); + put("%texture%", name); + put("%side%", name.replace("trapdoor", "door_side")); + } + } + ); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + boolean isTop = blockState.getValue(HALF) == Half.TOP; + boolean isOpen = blockState.getValue(OPEN); + int y = 0; + int x = (isTop && isOpen) ? 270 : isTop ? 180 : isOpen ? 90 : 0; + switch (blockState.getValue(FACING)) { + case EAST: + y = (isTop && isOpen) ? 270 : 90; + break; + case NORTH: + if (isTop && isOpen) y = 180; + break; + case SOUTH: + y = (isTop && isOpen) ? 0 : 180; + break; + case WEST: + y = (isTop && isOpen) ? 90 : 270; + break; + default: + break; + } + BlockModelRotation rotation = BlockModelRotation.by(x, y); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseUnderwaterWallPlantBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseUnderwaterWallPlantBlock.java new file mode 100644 index 00000000..0b1c08b0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseUnderwaterWallPlantBlock.java @@ -0,0 +1,61 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.LiquidBlockContainer; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.material.Material; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +public abstract class BaseUnderwaterWallPlantBlock extends BaseWallPlantBlock implements LiquidBlockContainer { + public BaseUnderwaterWallPlantBlock() { + this( + FabricBlockSettings + .of(Material.WATER_PLANT) + .sound(SoundType.WET_GRASS) + .noCollission() + ); + } + + public BaseUnderwaterWallPlantBlock(int light) { + this( + FabricBlockSettings + .of(Material.WATER_PLANT) + .luminance(light) + .sound(SoundType.WET_GRASS) + .noCollission() + ); + } + + public BaseUnderwaterWallPlantBlock(Properties settings) { + super(settings); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public FluidState getFluidState(BlockState state) { + return Fluids.WATER.getSource(false); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + return world.getFluidState(pos).getType() == Fluids.WATER && super.canSurvive(state, world, pos); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseVineBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseVineBlock.java new file mode 100644 index 00000000..a19379d9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseVineBlock.java @@ -0,0 +1,160 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BlockTags; +import net.minecraft.util.RandomSource; +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.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealableBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.blocks.BlockProperties.TripleShape; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.BlocksHelper; + +import java.util.List; +import java.util.function.Function; + +@SuppressWarnings("deprecation") +public class BaseVineBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock { + public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; + private static final VoxelShape VOXEL_SHAPE = box(2, 0, 2, 14, 16, 14); + + public BaseVineBlock() { + this(0, false); + } + + public BaseVineBlock(int light) { + this(light, false); + } + + public BaseVineBlock(int light, boolean bottomOnly) { + this(light, bottomOnly, p -> p); + } + + public BaseVineBlock(int light, boolean bottomOnly, Function propMod) { + this( + propMod.apply(FabricBlockSettings + .of(Material.PLANT) + .sound(SoundType.GRASS) + .lightLevel((state) -> bottomOnly ? state.getValue(SHAPE) == TripleShape.BOTTOM + ? light + : 0 : light) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ)) + ); + } + + public BaseVineBlock(BlockBehaviour.Properties properties) { + super(properties.offsetType(BlockBehaviour.OffsetType.XZ)); + this.registerDefaultState(this.stateDefinition.any().setValue(SHAPE, TripleShape.BOTTOM)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(SHAPE); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return VOXEL_SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + public boolean canGenerate(BlockState state, LevelReader world, BlockPos pos) { + return isSupport(state, world, pos); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + return isSupport(state, world, pos); + } + + protected boolean isSupport(BlockState state, LevelReader world, BlockPos pos) { + BlockState up = world.getBlockState(pos.above()); + return up.is(this) || up.is(BlockTags.LEAVES) || canSupportCenter(world, pos.above(), Direction.DOWN); + } + + @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 { + if (world.getBlockState(pos.below()).getBlock() != this) return state.setValue(SHAPE, TripleShape.BOTTOM); + else if (world.getBlockState(pos.above()).getBlock() != this) return state.setValue(SHAPE, TripleShape.TOP); + return state.setValue(SHAPE, TripleShape.MIDDLE); + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( + Enchantments.SILK_TOUCH, + tool + ) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } else { + return Lists.newArrayList(); + } + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + while (world.getBlockState(pos).getBlock() == this) { + pos = pos.below(); + } + return world.getBlockState(pos).isAir(); + } + + @Override + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + while (level.getBlockState(pos).getBlock() == this) { + pos = pos.below(); + } + return level.isEmptyBlock(pos); + } + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + while (level.getBlockState(pos).getBlock() == this) { + pos = pos.below(); + } + level.setBlockAndUpdate(pos, defaultBlockState()); + BlocksHelper.setWithoutUpdate(level, pos, defaultBlockState()); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseWallBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseWallBlock.java new file mode 100644 index 00000000..2d1f0385 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseWallBlock.java @@ -0,0 +1,125 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WallBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.WallSide; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseWallBlock extends WallBlock implements BlockModelProvider { + private final Block parent; + + public BaseWallBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + this.parent = source; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_WALL, parentId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + String path = blockId.getPath(); + Optional pattern = Optional.empty(); + if (path.endsWith("_post")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_WALL_POST, parentId); + } + if (path.endsWith("_side")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_WALL_SIDE, parentId); + } + if (path.endsWith("_side_tall")) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_WALL_SIDE_TALL, parentId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_post"); + ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_side"); + ResourceLocation sideTallId = new ResourceLocation( + stateId.getNamespace(), + "block/" + stateId.getPath() + "_side_tall" + ); + registerBlockModel(postId, postId, blockState, modelCache); + registerBlockModel(sideId, sideId, blockState, modelCache); + registerBlockModel(sideTallId, sideTallId, blockState, modelCache); + + ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); + builder.part(sideId).setCondition(state -> state.getValue(NORTH_WALL) == WallSide.LOW).setUVLock(true).add(); + builder.part(sideId) + .setCondition(state -> state.getValue(EAST_WALL) == WallSide.LOW) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideId) + .setCondition(state -> state.getValue(SOUTH_WALL) == WallSide.LOW) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideId) + .setCondition(state -> state.getValue(WEST_WALL) == WallSide.LOW) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideTallId) + .setCondition(state -> state.getValue(NORTH_WALL) == WallSide.TALL) + .setUVLock(true) + .add(); + builder.part(sideTallId) + .setCondition(state -> state.getValue(EAST_WALL) == WallSide.TALL) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideTallId) + .setCondition(state -> state.getValue(SOUTH_WALL) == WallSide.TALL) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()) + .setUVLock(true) + .add(); + builder.part(sideTallId) + .setCondition(state -> state.getValue(WEST_WALL) == WallSide.TALL) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()) + .setUVLock(true) + .add(); + builder.part(postId).setCondition(state -> state.getValue(UP)).add(); + + return builder.build(); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseWallPlantBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseWallPlantBlock.java new file mode 100644 index 00000000..86bbd93f --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseWallPlantBlock.java @@ -0,0 +1,125 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.betterx.bclib.util.BlocksHelper; + +import java.util.EnumMap; + +public abstract class BaseWallPlantBlock extends BasePlantBlock { + private static final EnumMap SHAPES = Maps.newEnumMap(ImmutableMap.of( + Direction.NORTH, box(1, 1, 8, 15, 15, 16), + Direction.SOUTH, box(1, 1, 0, 15, 15, 8), + Direction.WEST, box(8, 1, 1, 16, 15, 15), + Direction.EAST, box(0, 1, 1, 8, 15, 15) + )); + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + + public BaseWallPlantBlock() { + this( + FabricBlockSettings + .of(Material.PLANT) + .sound(SoundType.GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.NONE) + ); + } + + public BaseWallPlantBlock(int light) { + this( + FabricBlockSettings + .of(Material.PLANT) + .luminance(light) + .sound(SoundType.GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.NONE) + ); + } + + public BaseWallPlantBlock(Properties settings) { + super(settings.offsetType(BlockBehaviour.OffsetType.NONE)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(FACING); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPES.get(state.getValue(FACING)); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + Direction direction = state.getValue(FACING); + BlockPos blockPos = pos.relative(direction.getOpposite()); + BlockState blockState = world.getBlockState(blockPos); + return isSupport(world, blockPos, blockState, direction); + } + + public boolean isSupport(LevelReader world, BlockPos pos, BlockState blockState, Direction direction) { + return blockState.getMaterial().isSolid() && blockState.isFaceSturdy(world, pos, direction); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + BlockState blockState = this.defaultBlockState(); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + Direction[] directions = ctx.getNearestLookingDirections(); + for (Direction direction : directions) { + if (direction.getAxis().isHorizontal()) { + Direction direction2 = direction.getOpposite(); + blockState = blockState.setValue(FACING, direction2); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState; + } + } + } + return null; + } + + @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 + @SuppressWarnings("deprecation") + public BlockState rotate(BlockState state, Rotation rotation) { + return BlocksHelper.rotateHorizontal(state, rotation, FACING); + } + + @Override + @SuppressWarnings("deprecation") + public BlockState mirror(BlockState state, Mirror mirror) { + return BlocksHelper.mirrorHorizontal(state, mirror, FACING); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseWeightedPlateBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseWeightedPlateBlock.java new file mode 100644 index 00000000..8d33cb80 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseWeightedPlateBlock.java @@ -0,0 +1,78 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WeightedPressurePlateBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.BlockModelProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class BaseWeightedPlateBlock extends WeightedPressurePlateBlock implements BlockModelProvider { + private final Block parent; + + public BaseWeightedPlateBlock(Block source) { + super( + 15, + FabricBlockSettings.copyOf(source) + .noCollission() + .noOcclusion() + .requiresCorrectToolForDrops() + .strength(0.5F) + ); + this.parent = source; + } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (blockState.getValue(POWER) > 0) { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_DOWN, parentId); + } else { + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_UP, parentId); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + String state = blockState.getValue(POWER) > 0 ? "_down" : "_up"; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createBlockSimple(modelId); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BaseWoodenButtonBlock.java b/src/main/java/org/betterx/bclib/blocks/BaseWoodenButtonBlock.java new file mode 100644 index 00000000..96ef1246 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BaseWoodenButtonBlock.java @@ -0,0 +1,18 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.Block; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +public class BaseWoodenButtonBlock extends BaseButtonBlock { + public BaseWoodenButtonBlock(Block source) { + super(source, FabricBlockSettings.copyOf(source).strength(0.5F, 0.5F).noOcclusion(), true); + } + + @Override + protected SoundEvent getSound(boolean clicked) { + return clicked ? SoundEvents.WOODEN_BUTTON_CLICK_ON : SoundEvents.WOODEN_BUTTON_CLICK_OFF; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/BlockProperties.java b/src/main/java/org/betterx/bclib/blocks/BlockProperties.java new file mode 100644 index 00000000..115259b8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/BlockProperties.java @@ -0,0 +1,76 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; + +public class BlockProperties { + public static final EnumProperty TRIPLE_SHAPE = EnumProperty.create("shape", TripleShape.class); + public static final EnumProperty PENTA_SHAPE = EnumProperty.create("shape", PentaShape.class); + + public static final BooleanProperty TRANSITION = BooleanProperty.create("transition"); + public static final BooleanProperty HAS_LIGHT = BooleanProperty.create("has_light"); + public static final BooleanProperty IS_FLOOR = BooleanProperty.create("is_floor"); + public static final BooleanProperty NATURAL = BooleanProperty.create("natural"); + public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); + public static final BooleanProperty SMALL = BooleanProperty.create("small"); + + public static final IntegerProperty DEFAULT_ANVIL_DURABILITY = IntegerProperty.create("durability", 0, 3); + public static final IntegerProperty DESTRUCTION = IntegerProperty.create("destruction", 0, 2); + public static final IntegerProperty ROTATION = IntegerProperty.create("rotation", 0, 3); + public static final IntegerProperty FULLNESS = IntegerProperty.create("fullness", 0, 3); + public static final IntegerProperty COLOR = IntegerProperty.create("color", 0, 7); + public static final IntegerProperty SIZE = IntegerProperty.create("size", 0, 7); + public static final IntegerProperty AGE = IntegerProperty.create("age", 0, 3); + + public enum TripleShape implements StringRepresentable { + TOP("top", 0), MIDDLE("middle", 1), BOTTOM("bottom", 2); + + private final String name; + private final int index; + + TripleShape(String name, int index) { + this.name = name; + this.index = index; + } + + @Override + public String getSerializedName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public int getIndex() { + return index; + } + + public static TripleShape fromIndex(int index) { + return index > 1 ? BOTTOM : index == 1 ? MIDDLE : TOP; + } + } + + public enum PentaShape implements StringRepresentable { + BOTTOM("bottom"), PRE_BOTTOM("pre_bottom"), MIDDLE("middle"), PRE_TOP("pre_top"), TOP("top"); + + private final String name; + + PentaShape(String name) { + this.name = name; + } + + @Override + public String getSerializedName() { + return name; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/FeatureHangingSaplingBlock.java b/src/main/java/org/betterx/bclib/blocks/FeatureHangingSaplingBlock.java new file mode 100644 index 00000000..8d76cae9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/FeatureHangingSaplingBlock.java @@ -0,0 +1,42 @@ +package org.betterx.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.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import java.util.function.Function; + +public abstract class FeatureHangingSaplingBlock extends FeatureSaplingBlock { + private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12); + + public FeatureHangingSaplingBlock(Function> featureSupplier) { + super(featureSupplier); + } + + public FeatureHangingSaplingBlock(Function> featureSupplier, int light) { + super(light, featureSupplier); + } + + public FeatureHangingSaplingBlock(BlockBehaviour.Properties properties, + Function> featureSupplier) { + super(properties, featureSupplier); + } + + @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; + } + +} diff --git a/src/main/java/org/betterx/bclib/blocks/FeatureSaplingBlock.java b/src/main/java/org/betterx/bclib/blocks/FeatureSaplingBlock.java new file mode 100644 index 00000000..75af7933 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/FeatureSaplingBlock.java @@ -0,0 +1,146 @@ +package org.betterx.bclib.blocks; + +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.util.RandomSource; +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.BlockBehaviour; +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 net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.RenderLayerProvider; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + +public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProvider, BlockModelProvider { + private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); + private final Function> feature; + + public FeatureSaplingBlock(Function> featureSupplier) { + this(FabricBlockSettings.of(Material.PLANT) + .collidable(false) + .instabreak() + .sound(SoundType.GRASS) + .randomTicks(), + featureSupplier + ); + } + + public FeatureSaplingBlock(int light, Function> featureSupplier) { + this(FabricBlockSettings.of(Material.PLANT) + .collidable(false) + .luminance(light) + .instabreak() + .sound(SoundType.GRASS) + .randomTicks(), + featureSupplier + ); + } + + public FeatureSaplingBlock(BlockBehaviour.Properties properties, Function> featureSupplier) { + super(null, properties); + this.feature = featureSupplier; + } + + protected Feature getFeature(BlockState state) { + return feature.apply(state); + } + + @Override + public List 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 level, RandomSource random, BlockPos pos, BlockState state) { + return random.nextInt(16) == 0; + } + + @Override + public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, RandomSource random) { + FeaturePlaceContext context = new FeaturePlaceContext( + Optional.empty(), + world, + world.getChunkSource().getGenerator(), + random, + pos, + null + ); + getFeature(blockState).place(context); + } + + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + this.tick(state, world, pos, random); + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource 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 pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS, resourceLocation); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/LeveledAnvilBlock.java b/src/main/java/org/betterx/bclib/blocks/LeveledAnvilBlock.java new file mode 100644 index 00000000..9f900ed6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/LeveledAnvilBlock.java @@ -0,0 +1,16 @@ +package org.betterx.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; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/SimpleLeavesBlock.java b/src/main/java/org/betterx/bclib/blocks/SimpleLeavesBlock.java new file mode 100644 index 00000000..b29c7cf7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/SimpleLeavesBlock.java @@ -0,0 +1,67 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.material.MaterialColor; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.api.tag.NamedBlockTags; +import org.betterx.bclib.api.tag.NamedItemTags; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.interfaces.TagProvider; +import org.betterx.bclib.interfaces.tools.AddMineableHoe; +import org.betterx.bclib.interfaces.tools.AddMineableShears; + +import java.util.List; + +public class SimpleLeavesBlock extends BaseBlockNotFull implements RenderLayerProvider, TagProvider, AddMineableShears, AddMineableHoe { + public SimpleLeavesBlock(MaterialColor color) { + this( + FabricBlockSettings + .of(Material.LEAVES) + .strength(0.2F) + .color(color) + .sound(SoundType.GRASS) + .noOcclusion() + .isValidSpawn((state, world, pos, type) -> false) + .isSuffocating((state, world, pos) -> false) + .isViewBlocking((state, world, pos) -> false) + ); + } + + public SimpleLeavesBlock(MaterialColor color, int light) { + this( + FabricBlockSettings + .of(Material.LEAVES) + .luminance(light) + .color(color) + .strength(0.2F) + .sound(SoundType.GRASS) + .noOcclusion() + .isValidSpawn((state, world, pos, type) -> false) + .isSuffocating((state, world, pos) -> false) + .isViewBlocking((state, world, pos) -> false) + ); + } + + public SimpleLeavesBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + public void addTags(List> blockTags, List> itemTags) { + blockTags.add(NamedBlockTags.LEAVES); + itemTags.add(NamedItemTags.LEAVES); + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/blocks/StalactiteBlock.java b/src/main/java/org/betterx/bclib/blocks/StalactiteBlock.java new file mode 100644 index 00000000..3a73f1f5 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/StalactiteBlock.java @@ -0,0 +1,260 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlockContainer; +import net.minecraft.world.level.block.SimpleWaterloggedBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; + +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("deprecation") +public class StalactiteBlock extends BaseBlockNotFull implements SimpleWaterloggedBlock, LiquidBlockContainer, RenderLayerProvider { + public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + public static final BooleanProperty IS_FLOOR = BlockProperties.IS_FLOOR; + public static final IntegerProperty SIZE = BlockProperties.SIZE; + private static final VoxelShape[] SHAPES; + + public StalactiteBlock(Block source) { + this(FabricBlockSettings.copy(source).noOcclusion()); + } + + public StalactiteBlock(BlockBehaviour.Properties properties) { + super(properties); + this.registerDefaultState(getStateDefinition().any() + .setValue(SIZE, 0) + .setValue(IS_FLOOR, true) + .setValue(WATERLOGGED, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(WATERLOGGED, IS_FLOOR, SIZE); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPES[state.getValue(SIZE)]; + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + LevelReader world = ctx.getLevel(); + BlockPos pos = ctx.getClickedPos(); + Direction dir = ctx.getClickedFace(); + boolean water = world.getFluidState(pos).getType() == Fluids.WATER; + + if (dir == Direction.DOWN) { + if (isThis(world, pos.above()) || canSupportCenter(world, pos.above(), Direction.DOWN)) { + return defaultBlockState().setValue(IS_FLOOR, false).setValue(WATERLOGGED, water); + } else if (isThis(world, pos.below()) || canSupportCenter(world, pos.below(), Direction.UP)) { + return defaultBlockState().setValue(IS_FLOOR, true).setValue(WATERLOGGED, water); + } else { + return null; + } + } else { + if (isThis(world, pos.below()) || canSupportCenter(world, pos.below(), Direction.UP)) { + return defaultBlockState().setValue(IS_FLOOR, true).setValue(WATERLOGGED, water); + } else if (isThis(world, pos.above()) || canSupportCenter(world, pos.above(), Direction.DOWN)) { + return defaultBlockState().setValue(IS_FLOOR, false).setValue(WATERLOGGED, water); + } else { + return null; + } + } + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + boolean hasUp = isThis(world, pos.above()); + boolean hasDown = isThis(world, pos.below()); + MutableBlockPos mut = new MutableBlockPos(); + if (hasUp && hasDown) { + boolean floor = state.getValue(IS_FLOOR); + BlockPos second = floor ? pos.above() : pos.below(); + BlockState bState = world.getBlockState(second); + world.setBlockAndUpdate(pos, state.setValue(SIZE, 1).setValue(IS_FLOOR, floor)); + world.setBlockAndUpdate(second, bState.setValue(SIZE, 1).setValue(IS_FLOOR, !floor)); + + bState = state; + int startSize = floor ? 1 : 2; + mut.set(pos.getX(), pos.getY() + 1, pos.getZ()); + for (int i = 0; i < 8 && isThis(bState); i++) { + world.setBlockAndUpdate(mut, bState.setValue(SIZE, startSize++).setValue(IS_FLOOR, false)); + mut.setY(mut.getY() + 1); + bState = world.getBlockState(mut); + } + + bState = state; + startSize = floor ? 2 : 1; + mut.set(pos.getX(), pos.getY() - 1, pos.getZ()); + for (int i = 0; i < 8 && isThis(bState); i++) { + world.setBlockAndUpdate(mut, bState.setValue(SIZE, startSize++).setValue(IS_FLOOR, true)); + mut.setY(mut.getY() - 1); + bState = world.getBlockState(mut); + } + } else if (hasDown) { + mut.setX(pos.getX()); + mut.setZ(pos.getZ()); + for (int i = 1; i < 8; i++) { + mut.setY(pos.getY() - i); + if (isThis(world, mut)) { + BlockState state2 = world.getBlockState(mut); + int size = state2.getValue(SIZE); + if (size < i) { + world.setBlockAndUpdate(mut, state2.setValue(SIZE, i).setValue(IS_FLOOR, true)); + } else { + break; + } + } else { + break; + } + } + } else if (hasUp) { + mut.setX(pos.getX()); + mut.setZ(pos.getZ()); + for (int i = 1; i < 8; i++) { + mut.setY(pos.getY() + i); + if (isThis(world, mut)) { + BlockState state2 = world.getBlockState(mut); + int size = state2.getValue(SIZE); + if (size < i) { + world.setBlockAndUpdate(mut, state2.setValue(SIZE, i).setValue(IS_FLOOR, false)); + } else { + break; + } + } else { + break; + } + } + } + } + + private boolean isThis(LevelReader world, BlockPos pos) { + return isThis(world.getBlockState(pos)); + } + + private boolean isThis(BlockState state) { + return state.getBlock() instanceof StalactiteBlock; + } + + @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(); + } + return state; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + int size = state.getValue(SIZE); + return checkUp(world, pos, size) || checkDown(world, pos, size); + } + + private boolean checkUp(BlockGetter world, BlockPos pos, int size) { + BlockPos p = pos.above(); + BlockState state = world.getBlockState(p); + return (isThis(state) && state.getValue(SIZE) >= size) || state.isCollisionShapeFullBlock(world, p); + } + + private boolean checkDown(BlockGetter world, BlockPos pos, int size) { + BlockPos p = pos.below(); + BlockState state = world.getBlockState(p); + return (isThis(state) && state.getValue(SIZE) >= size) || state.isCollisionShapeFullBlock(world, p); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS_SHADED, resourceLocation); + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + BlockModelRotation rotation = blockState.getValue(IS_FLOOR) + ? BlockModelRotation.X0_Y0 + : BlockModelRotation.X180_Y0; + ResourceLocation modelId = new ResourceLocation( + stateId.getNamespace(), + stateId.getPath() + "_" + blockState.getValue(SIZE) + ); + registerBlockModel(modelId, modelId, blockState, modelCache); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return false; + } + + @Override + public FluidState getFluidState(BlockState state) { + return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : Fluids.EMPTY.defaultFluidState(); + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + static { + float end = 2F / 8F; + float start = 5F / 8F; + SHAPES = new VoxelShape[8]; + for (int i = 0; i < 8; i++) { + int side = Mth.floor(Mth.lerp(i / 7F, start, end) * 8F + 0.5F); + SHAPES[i] = box(side, 0, side, 16 - side, 16, 16 - side); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/blocks/StonePressurePlateBlock.java b/src/main/java/org/betterx/bclib/blocks/StonePressurePlateBlock.java new file mode 100644 index 00000000..9cd3ba24 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/StonePressurePlateBlock.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.world.level.block.Block; + +public class StonePressurePlateBlock extends BasePressurePlateBlock { + public StonePressurePlateBlock(Block source) { + super(Sensitivity.MOBS, source); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/StripableBarkBlock.java b/src/main/java/org/betterx/bclib/blocks/StripableBarkBlock.java new file mode 100644 index 00000000..ea3bd066 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/StripableBarkBlock.java @@ -0,0 +1,53 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.BlockHitResult; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.api.tag.NamedMineableTags; +import org.betterx.bclib.api.tag.TagAPI; + +public class StripableBarkBlock extends BaseBarkBlock { + private final Block striped; + + public StripableBarkBlock(MaterialColor color, Block striped) { + super(FabricBlockSettings.copyOf(striped).color(color)); + this.striped = striped; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, + Level world, + BlockPos pos, + Player player, + InteractionHand hand, + BlockHitResult hit) { + if (TagAPI.isToolWithMineableTag(player.getMainHandItem(), NamedMineableTags.AXE)) { + world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); + if (!world.isClientSide) { + world.setBlock(pos, + striped.defaultBlockState() + .setValue(AXIS, state.getValue(AXIS)), + 11 + ); + if (!player.isCreative()) { + player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); + } + } + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/TripleTerrainBlock.java b/src/main/java/org/betterx/bclib/blocks/TripleTerrainBlock.java new file mode 100644 index 00000000..8371e451 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/TripleTerrainBlock.java @@ -0,0 +1,177 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.MultiVariant; +import net.minecraft.client.renderer.block.model.Variant; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.BlockHitResult; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.blocks.BlockProperties.TripleShape; +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public class TripleTerrainBlock extends BaseTerrainBlock { + public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; + + public TripleTerrainBlock(Block baseBlock) { + super(baseBlock, baseBlock.defaultMaterialColor()); + this.registerDefaultState(defaultBlockState().setValue(SHAPE, TripleShape.BOTTOM)); + } + + public TripleTerrainBlock(Block baseBlock, MaterialColor color) { + super(baseBlock, color); + this.registerDefaultState(defaultBlockState().setValue(SHAPE, TripleShape.BOTTOM)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(SHAPE); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + Direction dir = ctx.getClickedFace(); + TripleShape shape = dir == Direction.UP + ? TripleShape.BOTTOM + : dir == Direction.DOWN ? TripleShape.TOP : TripleShape.MIDDLE; + return defaultBlockState().setValue(SHAPE, shape); + } + + @Override + public InteractionResult use(BlockState state, + Level world, + BlockPos pos, + Player player, + InteractionHand hand, + BlockHitResult hit) { + TripleShape shape = state.getValue(SHAPE); + if (shape == TripleShape.BOTTOM) { + return super.use(state, world, pos, player, hand, hit); + } + return InteractionResult.FAIL; + } + + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + TripleShape shape = state.getValue(SHAPE); + if (shape == TripleShape.BOTTOM) { + super.randomTick(state, world, pos, random); + } else if (random.nextInt(16) == 0) { + boolean bottom = canStayBottom(world, pos); + if (shape == TripleShape.TOP) { + if (!bottom) { + world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); + } + } else { + boolean top = canStay(state, world, pos) || isMiddle(world.getBlockState(pos.above())); + if (!top && !bottom) { + world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); + } else if (top && !bottom) { + world.setBlockAndUpdate(pos, state.setValue(SHAPE, TripleShape.BOTTOM)); + } else if (!top) { + world.setBlockAndUpdate(pos, state.setValue(SHAPE, TripleShape.TOP)); + } + } + } + } + + protected boolean canStayBottom(LevelReader world, BlockPos pos) { + BlockPos blockPos = pos.below(); + BlockState blockState = world.getBlockState(blockPos); + if (isMiddle(blockState)) { + return true; + } else if (blockState.getFluidState().getAmount() == 8) { + return false; + } else { + return !blockState.isFaceSturdy(world, blockPos, Direction.UP); + } + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + @Environment(EnvType.CLIENT) + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String path = blockId.getPath(); + Optional pattern; + if (isMiddle(blockState)) { + ResourceLocation topId = new ResourceLocation(blockId.getNamespace(), path + "_top"); + pattern = PatternsHelper.createBlockSimple(topId); + } else { + Map textures = Maps.newHashMap(); + textures.put("%top%", "betterend:block/" + path + "_top"); + textures.put("%side%", "betterend:block/" + path + "_side"); + textures.put("%bottom%", "minecraft:block/end_stone"); + pattern = PatternsHelper.createJson(BasePatterns.BLOCK_TOP_SIDE_BOTTOM, textures); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + @Environment(EnvType.CLIENT) + public UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + boolean isMiddle = isMiddle(blockState); + String middle = isMiddle ? "_middle" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + middle); + registerBlockModel(stateId, modelId, blockState, modelCache); + if (isMiddle) { + List variants = Lists.newArrayList(); + for (BlockModelRotation rotation : BlockModelRotation.values()) { + variants.add(new Variant(modelId, rotation.getRotation(), false, 1)); + } + return new MultiVariant(variants); + } else if (blockState.getValue(SHAPE) == TripleShape.TOP) { + return new MultiVariant(Lists.newArrayList( + new Variant( + modelId, + BlockModelRotation.X180_Y0.getRotation(), + false, + 1 + ), + new Variant(modelId, BlockModelRotation.X180_Y90.getRotation(), false, 1), + new Variant(modelId, BlockModelRotation.X180_Y180.getRotation(), false, 1), + new Variant(modelId, BlockModelRotation.X180_Y270.getRotation(), false, 1) + )); + } + return ModelsHelper.createRandomTopModel(modelId); + } + + protected boolean isMiddle(BlockState blockState) { + return blockState.is(this) && blockState.getValue(SHAPE) == TripleShape.MIDDLE; + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/UnderwaterPlantBlock.java b/src/main/java/org/betterx/bclib/blocks/UnderwaterPlantBlock.java new file mode 100644 index 00000000..c7dcad81 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/UnderwaterPlantBlock.java @@ -0,0 +1,166 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.item.ItemEntity; +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.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealableBlock; +import net.minecraft.world.level.block.LiquidBlockContainer; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.items.tool.BaseShearsItem; + +import java.util.List; +import java.util.function.Function; + +public abstract class UnderwaterPlantBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock, LiquidBlockContainer { + private static final VoxelShape SHAPE = box(4, 0, 4, 12, 14, 12); + + public UnderwaterPlantBlock() { + this(p -> p); + } + + public UnderwaterPlantBlock(Function propMod) { + this( + propMod.apply(FabricBlockSettings + .of(Material.WATER_PLANT) + .sound(SoundType.WET_GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ)) + ); + } + + public UnderwaterPlantBlock(int light) { + this(light, p -> p); + } + + public UnderwaterPlantBlock(int light, Function propMod) { + this( + propMod.apply(FabricBlockSettings + .of(Material.WATER_PLANT) + .luminance(light) + .sound(SoundType.WET_GRASS) + .noCollission() + .offsetType(BlockBehaviour.OffsetType.XZ)) + ); + } + + public UnderwaterPlantBlock(Properties settings) { + super(settings); + } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + @SuppressWarnings("deprecation") + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + state = world.getBlockState(pos); + return isTerrain(down) && state.getFluidState().getType().equals(Fluids.WATER.getSource()); + } + + protected abstract boolean isTerrain(BlockState state); + + @Override + @SuppressWarnings("deprecation") + public BlockState updateShape(BlockState state, + Direction facing, + BlockState neighborState, + LevelAccessor world, + BlockPos pos, + BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + world.destroyBlock(pos, true); + return Blocks.WATER.defaultBlockState(); + } else { + return state; + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( + Enchantments.SILK_TOUCH, + tool + ) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } else { + return Lists.newArrayList(); + } + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return true; + } + + @Override + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + ItemEntity item = new ItemEntity( + level, + pos.getX() + 0.5, + pos.getY() + 0.5, + pos.getZ() + 0.5, + new ItemStack(this) + ); + level.addFreshEntity(item); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public FluidState getFluidState(BlockState state) { + return Fluids.WATER.getSource(false); + } + +} diff --git a/src/main/java/org/betterx/bclib/blocks/UnderwaterPlantWithAgeBlock.java b/src/main/java/org/betterx/bclib/blocks/UnderwaterPlantWithAgeBlock.java new file mode 100644 index 00000000..ec6232c1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/UnderwaterPlantWithAgeBlock.java @@ -0,0 +1,56 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +public abstract class UnderwaterPlantWithAgeBlock extends UnderwaterPlantBlock { + public static final IntegerProperty AGE = BlockProperties.AGE; + + public UnderwaterPlantWithAgeBlock() { + super( + FabricBlockSettings + .of(Material.WATER_PLANT) + .sound(SoundType.WET_GRASS) + .randomTicks() + .noCollission() + ); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(AGE); + } + + public abstract void grow(WorldGenLevel world, RandomSource random, BlockPos pos); + + @Override + public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { + if (random.nextInt(4) == 0) { + int age = state.getValue(AGE); + if (age < 3) { + world.setBlockAndUpdate(pos, state.setValue(AGE, age + 1)); + } else { + grow(world, random, pos); + } + } + } + + @Override + @SuppressWarnings("deprecation") + public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + super.tick(state, world, pos, random); + if (isBonemealSuccess(world, random, pos, state)) { + performBonemeal(world, random, pos, state); + } + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/UpDownPlantBlock.java b/src/main/java/org/betterx/bclib/blocks/UpDownPlantBlock.java new file mode 100644 index 00000000..8a0a9428 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/UpDownPlantBlock.java @@ -0,0 +1,113 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.player.Player; +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.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; +import org.betterx.bclib.client.render.BCLRenderLayer; +import org.betterx.bclib.interfaces.RenderLayerProvider; +import org.betterx.bclib.interfaces.tools.AddMineableHoe; +import org.betterx.bclib.interfaces.tools.AddMineableShears; +import org.betterx.bclib.items.tool.BaseShearsItem; + +import java.util.List; + +public abstract class UpDownPlantBlock extends BaseBlockNotFull implements RenderLayerProvider, AddMineableShears, AddMineableHoe { + private static final VoxelShape SHAPE = box(4, 0, 4, 12, 16, 12); + + public UpDownPlantBlock() { + this(FabricBlockSettings + .of(Material.PLANT) + .sound(SoundType.GRASS) + .noCollission() + ); + } + + public UpDownPlantBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + protected abstract boolean isTerrain(BlockState state); + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + @SuppressWarnings("deprecation") + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + BlockState up = world.getBlockState(pos.above()); + return (isTerrain(down) || down.getBlock() == this) && (isSupport(up, world, pos) || up.getBlock() == this); + } + + protected boolean isSupport(BlockState state, LevelReader world, BlockPos pos) { + return canSupportCenter(world, pos.above(), Direction.UP); + } + + @Override + @SuppressWarnings("deprecation") + 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 List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( + Enchantments.SILK_TOUCH, + tool + ) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } else { + return Lists.newArrayList(); + } + } + + @Override + public BCLRenderLayer getRenderLayer() { + return BCLRenderLayer.CUTOUT; + } + + @Override + public void playerDestroy(Level world, + Player player, + BlockPos pos, + BlockState state, + BlockEntity blockEntity, + ItemStack stack) { + super.playerDestroy(world, player, pos, state, blockEntity, stack); + world.neighborChanged(pos, Blocks.AIR, pos.below()); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/WallMushroomBlock.java b/src/main/java/org/betterx/bclib/blocks/WallMushroomBlock.java new file mode 100644 index 00000000..770e99d8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/WallMushroomBlock.java @@ -0,0 +1,45 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import com.google.common.collect.Lists; + +import java.util.List; + +public abstract class WallMushroomBlock extends BaseWallPlantBlock { + public WallMushroomBlock(int light) { + this( + FabricBlockSettings + .of(Material.PLANT) + .luminance(light) + .destroyTime(0.2F) + .sound(SoundType.GRASS) + .sound(SoundType.WOOD) + .noCollission() + ); + } + + public WallMushroomBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Lists.newArrayList(new ItemStack(this)); + } + + @Override + public boolean isSupport(LevelReader world, BlockPos pos, BlockState blockState, Direction direction) { + return blockState.getMaterial().isSolid() && blockState.isFaceSturdy(world, pos, direction); + } +} diff --git a/src/main/java/org/betterx/bclib/blocks/WoodenPressurePlateBlock.java b/src/main/java/org/betterx/bclib/blocks/WoodenPressurePlateBlock.java new file mode 100644 index 00000000..2a64768a --- /dev/null +++ b/src/main/java/org/betterx/bclib/blocks/WoodenPressurePlateBlock.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.blocks; + +import net.minecraft.world.level.block.Block; + +public class WoodenPressurePlateBlock extends BasePressurePlateBlock { + public WoodenPressurePlateBlock(Block source) { + super(Sensitivity.EVERYTHING, source); + } +} diff --git a/src/main/java/org/betterx/bclib/client/BCLibClient.java b/src/main/java/org/betterx/bclib/client/BCLibClient.java new file mode 100644 index 00000000..2c8d7953 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/BCLibClient.java @@ -0,0 +1,45 @@ +package org.betterx.bclib.client; + +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.model.*; + +import org.betterx.bclib.api.ModIntegrationAPI; +import org.betterx.bclib.api.PostInitAPI; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.client.models.CustomModelBakery; +import org.betterx.bclib.registry.BaseBlockEntityRenders; + +import org.jetbrains.annotations.Nullable; + +public class BCLibClient implements ClientModInitializer, ModelResourceProvider, ModelVariantProvider { + public static CustomModelBakery modelBakery; + + @Override + public void onInitializeClient() { + ModIntegrationAPI.registerAll(); + BaseBlockEntityRenders.register(); + DataExchangeAPI.prepareClientside(); + PostInitAPI.postInit(true); + modelBakery = new CustomModelBakery(); + ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> this); + ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> this); + } + + @Override + public @Nullable UnbakedModel loadModelResource(ResourceLocation resourceId, + ModelProviderContext context) throws ModelProviderException { + return modelBakery.getBlockModel(resourceId); + } + + @Override + public @Nullable UnbakedModel loadModelVariant(ModelResourceLocation modelId, + ModelProviderContext context) throws ModelProviderException { + return modelId.getVariant().equals("inventory") + ? modelBakery.getItemModel(modelId) + : modelBakery.getBlockModel(modelId); + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/BaseChestBlockModel.java b/src/main/java/org/betterx/bclib/client/models/BaseChestBlockModel.java new file mode 100644 index 00000000..df02f422 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/BaseChestBlockModel.java @@ -0,0 +1,112 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; + +public class BaseChestBlockModel { + public final ModelPart partA; + public final ModelPart partC; + public final ModelPart partB; + public final ModelPart partRightA; + public final ModelPart partRightC; + public final ModelPart partRightB; + public final ModelPart partLeftA; + public final ModelPart partLeftC; + public final ModelPart partLeftB; + + public static LayerDefinition getTexturedModelData() { + MeshDefinition modelData = new MeshDefinition(); + PartDefinition modelPartData = modelData.getRoot(); + CubeDeformation deformation_partC = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partC", + CubeListBuilder.create().texOffs(0, 19).addBox(1.0f, 0.0f, 1.0f, 14.0f, 9.0f, 14.0f, deformation_partC), + PartPose.ZERO + ); + + CubeDeformation deformation_partA = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partA", + CubeListBuilder.create().texOffs(0, 0).addBox(1.0f, 0.0f, 0.0f, 14.0f, 5.0f, 14.0f, deformation_partA), + PartPose.offset(0.0f, 9.0f, 1.0f) + ); + + CubeDeformation deformation_partB = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partB", + CubeListBuilder.create().texOffs(0, 0).addBox(7.0f, -1.0f, 15.0f, 2.0f, 4.0f, 1.0f, deformation_partB), + PartPose.offset(0.0f, 8.0f, 0.0f) + ); + + CubeDeformation deformation_partRightC = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partRightC", + CubeListBuilder.create() + .texOffs(0, 19) + .addBox(1.0f, 0.0f, 1.0f, 15.0f, 9.0f, 14.0f, deformation_partRightC), + PartPose.ZERO + ); + + CubeDeformation deformation_partRightA = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partRightA", + CubeListBuilder.create() + .texOffs(0, 0) + .addBox(1.0f, 0.0f, 0.0f, 15.0f, 5.0f, 14.0f, deformation_partRightA), + PartPose.offset(0.0f, 9.0f, 1.0f) + ); + + CubeDeformation deformation_partRightB = new CubeDeformation(0.0f); + PartDefinition partRightB = modelPartData.addOrReplaceChild( + "partRightB", + CubeListBuilder.create() + .texOffs(0, 0) + .addBox(15.0f, -1.0f, 15.0f, 1.0f, 4.0f, 1.0f, deformation_partRightB), + PartPose.offset(0.0f, 8.0f, 0.0f) + ); + + CubeDeformation deformation_partLeftC = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partLeftC", + CubeListBuilder.create() + .texOffs(0, 19) + .addBox(0.0f, 0.0f, 1.0f, 15.0f, 9.0f, 14.0f, deformation_partLeftC), + PartPose.ZERO + ); + + CubeDeformation deformation_partLeftA = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partLeftA", + CubeListBuilder.create() + .texOffs(0, 0) + .addBox(0.0f, 0.0f, 0.0f, 15.0f, 5.0f, 14.0f, deformation_partLeftA), + PartPose.offset(0.0f, 9.0f, 1.0f) + ); + + CubeDeformation deformation_partLeftB = new CubeDeformation(0.0f); + modelPartData.addOrReplaceChild( + "partLeftB", + CubeListBuilder.create() + .texOffs(0, 0) + .addBox(0.0f, -1.0f, 15.0f, 1.0f, 4.0f, 1.0f, deformation_partLeftB), + PartPose.offset(0.0f, 8.0f, 0.0f) + ); + + return LayerDefinition.create(modelData, 64, 64); + } + + public BaseChestBlockModel(ModelPart modelPart) { + super(); + + partC = modelPart.getChild("partC"); + partA = modelPart.getChild("partA"); + partB = modelPart.getChild("partB"); + partRightC = modelPart.getChild("partRightC"); + partRightA = modelPart.getChild("partRightA"); + partRightB = modelPart.getChild("partRightB"); + partLeftC = modelPart.getChild("partLeftC"); + partLeftA = modelPart.getChild("partLeftA"); + partLeftB = modelPart.getChild("partLeftB"); + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/BasePatterns.java b/src/main/java/org/betterx/bclib/client/models/BasePatterns.java new file mode 100644 index 00000000..1caacad9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/BasePatterns.java @@ -0,0 +1,62 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.resources.ResourceLocation; + +import org.betterx.bclib.BCLib; + +public class BasePatterns { + //Block Models + public final static ResourceLocation BLOCK_EMPTY = BCLib.makeID("patterns/block/empty.json"); + public final static ResourceLocation BLOCK_BASE = BCLib.makeID("patterns/block/block.json"); + public final static ResourceLocation BLOCK_SIDED = BCLib.makeID("patterns/block/block_sided.json"); + public final static ResourceLocation BLOCK_BOTTOM_TOP = BCLib.makeID("patterns/block/block_bottom_top.json"); + public final static ResourceLocation BLOCK_SLAB = BCLib.makeID("patterns/block/slab.json"); + public final static ResourceLocation BLOCK_STAIR = BCLib.makeID("patterns/block/stairs.json"); + public final static ResourceLocation BLOCK_STAIR_INNER = BCLib.makeID("patterns/block/stairs_inner.json"); + public final static ResourceLocation BLOCK_STAIR_OUTER = BCLib.makeID("patterns/block/stairs_outer.json"); + public final static ResourceLocation BLOCK_WALL_POST = BCLib.makeID("patterns/block/wall_post.json"); + public final static ResourceLocation BLOCK_WALL_SIDE = BCLib.makeID("patterns/block/wall_side.json"); + public final static ResourceLocation BLOCK_WALL_SIDE_TALL = BCLib.makeID("patterns/block/wall_side_tall.json"); + public final static ResourceLocation BLOCK_FENCE_POST = BCLib.makeID("patterns/block/fence_post.json"); + public final static ResourceLocation BLOCK_FENCE_SIDE = BCLib.makeID("patterns/block/fence_side.json"); + public final static ResourceLocation BLOCK_BUTTON = BCLib.makeID("patterns/block/button.json"); + public final static ResourceLocation BLOCK_BUTTON_PRESSED = BCLib.makeID("patterns/block/button_pressed.json"); + public final static ResourceLocation BLOCK_PILLAR = BCLib.makeID("patterns/block/pillar.json"); + public final static ResourceLocation BLOCK_PLATE_UP = BCLib.makeID("patterns/block/pressure_plate_up.json"); + public final static ResourceLocation BLOCK_PLATE_DOWN = BCLib.makeID("patterns/block/pressure_plate_down.json"); + public final static ResourceLocation BLOCK_DOOR_TOP = BCLib.makeID("patterns/block/door_top.json"); + public final static ResourceLocation BLOCK_DOOR_TOP_HINGE = BCLib.makeID("patterns/block/door_top_hinge.json"); + public final static ResourceLocation BLOCK_DOOR_BOTTOM = BCLib.makeID("patterns/block/door_bottom.json"); + public final static ResourceLocation BLOCK_DOOR_BOTTOM_HINGE = BCLib.makeID("patterns/block/door_bottom_hinge.json"); + public final static ResourceLocation BLOCK_CROSS = BCLib.makeID("patterns/block/cross.json"); + public final static ResourceLocation BLOCK_CROSS_SHADED = BCLib.makeID("patterns/block/cross_shaded.json"); + public final static ResourceLocation BLOCK_GATE_CLOSED = BCLib.makeID("patterns/block/fence_gate_closed.json"); + public final static ResourceLocation BLOCK_GATE_CLOSED_WALL = BCLib.makeID("patterns/block/wall_gate_closed.json"); + public final static ResourceLocation BLOCK_GATE_OPEN = BCLib.makeID("patterns/block/fence_gate_open.json"); + public final static ResourceLocation BLOCK_GATE_OPEN_WALL = BCLib.makeID("patterns/block/wall_gate_open.json"); + public final static ResourceLocation BLOCK_TRAPDOOR = BCLib.makeID("patterns/block/trapdoor.json"); + public final static ResourceLocation BLOCK_LADDER = BCLib.makeID("patterns/block/ladder.json"); + public final static ResourceLocation BLOCK_BARREL_OPEN = BCLib.makeID("patterns/block/barrel_open.json"); + public final static ResourceLocation BLOCK_BOOKSHELF = BCLib.makeID("patterns/block/bookshelf.json"); + public final static ResourceLocation BLOCK_COMPOSTER = BCLib.makeID("patterns/block/composter.json"); + public final static ResourceLocation BLOCK_COLORED = BCLib.makeID("patterns/block/block_colored.json"); + public final static ResourceLocation BLOCK_BARS_POST = BCLib.makeID("patterns/block/bars_post.json"); + public final static ResourceLocation BLOCK_BARS_SIDE = BCLib.makeID("patterns/block/bars_side.json"); + public final static ResourceLocation BLOCK_ANVIL = BCLib.makeID("patterns/block/anvil.json"); + public final static ResourceLocation BLOCK_CHAIN = BCLib.makeID("patterns/block/chain.json"); + public final static ResourceLocation BLOCK_FURNACE = BCLib.makeID("patterns/block/furnace.json"); + public final static ResourceLocation BLOCK_FURNACE_LIT = BCLib.makeID("patterns/block/furnace_glow.json"); + public final static ResourceLocation BLOCK_TOP_SIDE_BOTTOM = BCLib.makeID("patterns/block/top_side_bottom.json"); + public final static ResourceLocation BLOCK_PATH = BCLib.makeID("patterns/block/path.json"); + + //Item Models + public final static ResourceLocation ITEM_WALL = BCLib.makeID("patterns/item/pattern_wall.json"); + public final static ResourceLocation ITEM_FENCE = BCLib.makeID("patterns/item/pattern_fence.json"); + public final static ResourceLocation ITEM_BUTTON = BCLib.makeID("patterns/item/pattern_button.json"); + public final static ResourceLocation ITEM_CHEST = BCLib.makeID("patterns/item/pattern_chest.json"); + public final static ResourceLocation ITEM_BLOCK = BCLib.makeID("patterns/item/pattern_block_item.json"); + public final static ResourceLocation ITEM_GENERATED = BCLib.makeID("patterns/item/pattern_item_generated.json"); + public final static ResourceLocation ITEM_HANDHELD = BCLib.makeID("patterns/item/pattern_item_handheld.json"); + public final static ResourceLocation ITEM_SPAWN_EGG = BCLib.makeID("patterns/item/pattern_item_spawn_egg.json"); + +} diff --git a/src/main/java/org/betterx/bclib/client/models/CustomModelBakery.java b/src/main/java/org/betterx/bclib/client/models/CustomModelBakery.java new file mode 100644 index 00000000..198fb205 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/CustomModelBakery.java @@ -0,0 +1,139 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.multipart.MultiPart; +import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.mojang.datafixers.util.Pair; +import org.betterx.bclib.api.ModIntegrationAPI; +import org.betterx.bclib.client.render.EmissiveTextureInfo; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.ItemModelProvider; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class CustomModelBakery { + private final Map models = Maps.newConcurrentMap(); + + public UnbakedModel getBlockModel(ResourceLocation location) { + return models.get(location); + } + + public UnbakedModel getItemModel(ResourceLocation location) { + ResourceLocation storageID = new ResourceLocation(location.getNamespace(), + "models/item/" + location.getPath() + ".json"); + return models.get(location); + } + + public void loadCustomModels(ResourceManager resourceManager) { + Registry.BLOCK.stream().parallel().filter(block -> block instanceof BlockModelProvider).forEach(block -> { + ResourceLocation blockID = Registry.BLOCK.getKey(block); + ResourceLocation storageID = new ResourceLocation(blockID.getNamespace(), + "blockstates/" + blockID.getPath() + ".json"); + if (resourceManager.getResource(storageID).isEmpty()) { + addBlockModel(blockID, block); + } + storageID = new ResourceLocation(blockID.getNamespace(), "models/item/" + blockID.getPath() + ".json"); + if (resourceManager.getResource(storageID).isEmpty()) { + addItemModel(blockID, (ItemModelProvider) block); + } + }); + + Registry.ITEM.stream().parallel().filter(item -> item instanceof ItemModelProvider).forEach(item -> { + ResourceLocation registryID = Registry.ITEM.getKey(item); + ResourceLocation storageID = new ResourceLocation(registryID.getNamespace(), + "models/item/" + registryID.getPath() + ".json"); + if (resourceManager.getResource(storageID).isEmpty()) { + addItemModel(registryID, (ItemModelProvider) item); + } + }); + } + + private void addBlockModel(ResourceLocation blockID, Block block) { + BlockModelProvider provider = (BlockModelProvider) block; + ImmutableList states = block.getStateDefinition().getPossibleStates(); + BlockState defaultState = block.defaultBlockState(); + + ResourceLocation defaultStateID = BlockModelShaper.stateToModelLocation(blockID, defaultState); + UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, models); + + if (defaultModel instanceof MultiPart) { + states.forEach(blockState -> { + ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); + models.put(stateID, defaultModel); + }); + } else { + states.forEach(blockState -> { + ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); + UnbakedModel model = stateID.equals(defaultStateID) + ? defaultModel + : provider.getModelVariant(stateID, blockState, models); + models.put(stateID, model); + }); + } + } + + private void addItemModel(ResourceLocation itemID, ItemModelProvider provider) { + ModelResourceLocation modelLocation = new ModelResourceLocation(itemID.getNamespace(), + itemID.getPath(), + "inventory"); + if (models.containsKey(modelLocation)) { + return; + } + BlockModel model = provider.getItemModel(modelLocation); + models.put(modelLocation, model); + } + + public static void loadEmissiveModels(Map unbakedCache) { + if (!ModIntegrationAPI.hasCanvas()) { + return; + } + + Map cacheCopy = new HashMap<>(unbakedCache); + Set> strings = Sets.newConcurrentHashSet(); + Registry.BLOCK.keySet().forEach(blockID -> { + Block block = Registry.BLOCK.get(blockID); + ImmutableList states = block.getStateDefinition().getPossibleStates(); + boolean addBlock = false; + + for (BlockState state : states) { + ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, state); + UnbakedModel model = cacheCopy.get(stateID); + if (model == null) { + continue; + } + Collection materials = model.getMaterials(cacheCopy::get, strings); + if (materials == null) { + continue; + } + for (Material material : materials) { + if (EmissiveTextureInfo.isEmissiveTexture(material.texture())) { + addBlock = true; + break; + } + } + if (addBlock) { + break; + } + } + + if (addBlock) { + EmissiveTextureInfo.addBlock(blockID); + } + }); + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/CustomModelData.java b/src/main/java/org/betterx/bclib/client/models/CustomModelData.java new file mode 100644 index 00000000..35698fbf --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/CustomModelData.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.resources.ResourceLocation; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class CustomModelData { + private static final Set TRANSPARENT_EMISSION = Sets.newConcurrentHashSet(); + + public static void clear() { + TRANSPARENT_EMISSION.clear(); + } + + public static void addTransparent(ResourceLocation blockID) { + TRANSPARENT_EMISSION.add(blockID); + } + + public static boolean isTransparentEmissive(ResourceLocation rawLocation) { + String name = rawLocation.getPath().replace("materialmaps/block/", "").replace(".json", ""); + return TRANSPARENT_EMISSION.contains(new ResourceLocation(rawLocation.getNamespace(), name)); + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/ModelsHelper.java b/src/main/java/org/betterx/bclib/client/models/ModelsHelper.java new file mode 100644 index 00000000..8486cfc4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/ModelsHelper.java @@ -0,0 +1,162 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.MultiVariant; +import net.minecraft.client.renderer.block.model.Variant; +import net.minecraft.client.renderer.block.model.multipart.Condition; +import net.minecraft.client.renderer.block.model.multipart.MultiPart; +import net.minecraft.client.renderer.block.model.multipart.Selector; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Lists; +import com.mojang.math.Transformation; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +@Environment(EnvType.CLIENT) +public class ModelsHelper { + public static BlockModel fromPattern(Optional pattern) { + return pattern.map(BlockModel::fromString).orElse(null); + } + + public static BlockModel createItemModel(ResourceLocation resourceLocation) { + return fromPattern(PatternsHelper.createItemGenerated(resourceLocation)); + } + + public static BlockModel createHandheldItem(ResourceLocation resourceLocation) { + return fromPattern(PatternsHelper.createItemHandheld(resourceLocation)); + } + + public static BlockModel createBlockItem(ResourceLocation resourceLocation) { + Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_BLOCK, resourceLocation); + return fromPattern(pattern); + } + + public static BlockModel createBlockEmpty(ResourceLocation resourceLocation) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_EMPTY, resourceLocation); + return fromPattern(pattern); + } + + public static MultiVariant createMultiVariant(ResourceLocation resourceLocation, + Transformation transform, + boolean uvLock) { + Variant variant = new Variant(resourceLocation, transform, uvLock, 1); + return new MultiVariant(Lists.newArrayList(variant)); + } + + public static MultiVariant createBlockSimple(ResourceLocation resourceLocation) { + return createMultiVariant(resourceLocation, Transformation.identity(), false); + } + + public static MultiVariant createFacingModel(ResourceLocation resourceLocation, + Direction facing, + boolean uvLock, + boolean inverted) { + if (inverted) { + facing = facing.getOpposite(); + } + BlockModelRotation rotation = BlockModelRotation.by(0, (int) facing.toYRot()); + return createMultiVariant(resourceLocation, rotation.getRotation(), uvLock); + } + + public static MultiVariant createRotatedModel(ResourceLocation resourceLocation, Direction.Axis axis) { + BlockModelRotation rotation = BlockModelRotation.X0_Y0; + switch (axis) { + case X: + rotation = BlockModelRotation.X90_Y90; + break; + case Z: + rotation = BlockModelRotation.X90_Y0; + break; + default: + break; + } + return createMultiVariant(resourceLocation, rotation.getRotation(), false); + } + + public static MultiVariant createRandomTopModel(ResourceLocation resourceLocation) { + return new MultiVariant(Lists.newArrayList( + new Variant(resourceLocation, Transformation.identity(), false, 1), + new Variant(resourceLocation, BlockModelRotation.X0_Y90.getRotation(), false, 1), + new Variant(resourceLocation, BlockModelRotation.X0_Y180.getRotation(), false, 1), + new Variant(resourceLocation, BlockModelRotation.X0_Y270.getRotation(), false, 1) + )); + } + + public static class MultiPartBuilder { + + //private final static MultiPartBuilder BUILDER = new MultiPartBuilder(); + + public static MultiPartBuilder create(StateDefinition stateDefinition) { + // BUILDER.stateDefinition = stateDefinition; + //BUILDER.modelParts.clear(); + // return BUILDER; + return new MultiPartBuilder(stateDefinition); + } + + private final List modelParts = Lists.newArrayList(); + private final StateDefinition stateDefinition; + + private MultiPartBuilder(StateDefinition stateDefinition) { + this.stateDefinition = stateDefinition; + } + + public ModelPart part(ResourceLocation modelId) { + ModelPart part = new ModelPart(modelId); + return part; + } + + public MultiPart build() { + if (modelParts.size() > 0) { + List selectors = Lists.newArrayList(); + modelParts.forEach(modelPart -> { + MultiVariant variant = createMultiVariant(modelPart.modelId, modelPart.transform, modelPart.uvLock); + selectors.add(new Selector(modelPart.condition, variant)); + }); + modelParts.clear(); + return new MultiPart(stateDefinition, selectors); + } + throw new IllegalStateException("At least one model part need to be created."); + } + + public class ModelPart { + private final ResourceLocation modelId; + private Transformation transform = Transformation.identity(); + private Condition condition = Condition.TRUE; + private boolean uvLock = false; + + private ModelPart(ResourceLocation modelId) { + this.modelId = modelId; + } + + public ModelPart setCondition(Function condition) { + this.condition = stateDefinition -> condition::apply; + return this; + } + + public ModelPart setTransformation(Transformation transform) { + this.transform = transform; + return this; + } + + public ModelPart setUVLock(boolean value) { + this.uvLock = value; + return this; + } + + public void add() { + modelParts.add(this); + } + } + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/OBJBlockModel.java b/src/main/java/org/betterx/bclib/client/models/OBJBlockModel.java new file mode 100644 index 00000000..0b1e3085 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/OBJBlockModel.java @@ -0,0 +1,294 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemOverrides; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.*; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mojang.datafixers.util.Pair; +import com.mojang.math.Vector3f; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.util.BlocksHelper; +import org.betterx.bclib.util.MHelper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.*; +import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + +@Environment(EnvType.CLIENT) +public class OBJBlockModel implements UnbakedModel, BakedModel { + private static final Vector3f[] POSITIONS = new Vector3f[]{new Vector3f(), new Vector3f(), new Vector3f()}; + + protected final Map> quadsUnbakedMap = Maps.newEnumMap(Direction.class); + protected final Map> quadsBakedMap = Maps.newEnumMap(Direction.class); + protected final List quadsUnbaked = Lists.newArrayList(); + protected final List quadsBaked = Lists.newArrayList(); + + protected TextureAtlasSprite[] sprites; + protected ItemTransforms transforms; + protected ItemOverrides overrides; + + protected List materials; + protected boolean useCulling; + protected boolean useShading; + protected byte particleIndex; + + public OBJBlockModel(ResourceLocation location, + Vector3f offset, + boolean useCulling, + boolean useShading, + byte particleIndex, + ResourceLocation... textureIDs) { + for (Direction dir : BlocksHelper.DIRECTIONS) { + quadsUnbakedMap.put(dir, Lists.newArrayList()); + quadsBakedMap.put(dir, Lists.newArrayList()); + } + + transforms = ItemTransforms.NO_TRANSFORMS; + overrides = ItemOverrides.EMPTY; + materials = new ArrayList<>(textureIDs.length); + sprites = new TextureAtlasSprite[textureIDs.length]; + this.particleIndex = particleIndex; + this.useCulling = useCulling; + this.useShading = useShading; + loadModel(location, offset, (byte) (textureIDs.length - 1)); + + for (int i = 0; i < textureIDs.length; i++) { + materials.add(new Material(TextureAtlas.LOCATION_BLOCKS, textureIDs[i])); + } + } + + // UnbakedModel // + + @Override + public Collection getDependencies() { + return Collections.emptyList(); + } + + @Override + public Collection getMaterials(Function function, + Set> set) { + return materials; + } + + @Nullable + @Override + public BakedModel bake(ModelBakery modelBakery, + Function textureGetter, + ModelState modelState, + ResourceLocation resourceLocation) { + for (int i = 0; i < sprites.length; i++) { + sprites[i] = textureGetter.apply(materials.get(i)); + } + quadsBaked.clear(); + quadsUnbaked.forEach(quad -> quadsBaked.add(quad.bake(sprites, modelState))); + for (Direction dir : BlocksHelper.DIRECTIONS) { + List unbaked = quadsUnbakedMap.get(dir); + List baked = quadsBakedMap.get(dir); + baked.clear(); + unbaked.forEach(quad -> baked.add(quad.bake(sprites, modelState))); + } + return this; + } + + // Baked Model // + + @Override + public List getQuads(@Nullable BlockState blockState, + @Nullable Direction direction, + RandomSource random) { + return direction == null ? quadsBaked : quadsBakedMap.get(direction); + } + + @Override + public boolean useAmbientOcclusion() { + return true; + } + + @Override + public boolean isGui3d() { + return true; + } + + @Override + public boolean usesBlockLight() { + return true; + } + + @Override + public boolean isCustomRenderer() { + return false; + } + + @Override + public TextureAtlasSprite getParticleIcon() { + return sprites[particleIndex]; + } + + @Override + public ItemTransforms getTransforms() { + return transforms; + } + + @Override + public ItemOverrides getOverrides() { + return overrides; + } + + private Resource getResource(ResourceLocation location) { + return Minecraft.getInstance().getResourceManager().getResource(location).orElse(null); + } + + private void loadModel(ResourceLocation location, Vector3f offset, byte maxIndex) { + Resource resource = getResource(location); + if (resource == null) { + return; + } + InputStream input = null; + try { + input = resource.open(); + } catch (IOException e) { + BCLib.LOGGER.error("Unable to load Model", e); + throw new RuntimeException(e); + } + + List vertecies = new ArrayList<>(12); + List uvs = new ArrayList<>(8); + + List vertexIndex = new ArrayList<>(4); + List uvIndex = new ArrayList<>(4); + + byte materialIndex = -1; + + try { + InputStreamReader streamReader = new InputStreamReader(input); + BufferedReader reader = new BufferedReader(streamReader); + String string; + + while ((string = reader.readLine()) != null) { + if (string.startsWith("usemtl")) { + materialIndex++; + if (materialIndex > maxIndex) { + materialIndex = maxIndex; + } + } else if (string.startsWith("vt")) { + String[] uv = string.split(" "); + uvs.add(Float.parseFloat(uv[1])); + uvs.add(Float.parseFloat(uv[2])); + } else if (string.startsWith("v")) { + String[] vert = string.split(" "); + for (int i = 1; i < 4; i++) { + vertecies.add(Float.parseFloat(vert[i])); + } + } else if (string.startsWith("f")) { + String[] members = string.split(" "); + if (members.length != 5) { + System.out.println("Only quads in OBJ are supported! Model [" + location + "] has n-gons or triangles!"); + continue; + } + vertexIndex.clear(); + uvIndex.clear(); + + for (int i = 1; i < members.length; i++) { + String member = members[i]; + + if (member.contains("/")) { + String[] sub = member.split("/"); + vertexIndex.add(Integer.parseInt(sub[0]) - 1); // Vertex + uvIndex.add(Integer.parseInt(sub[1]) - 1); // UV + } else { + vertexIndex.add(Integer.parseInt(member) - 1); // Vertex + } + } + + boolean hasUV = !uvIndex.isEmpty(); + UnbakedQuad quad = new UnbakedQuad(); + for (int i = 0; i < 4; i++) { + int index = vertexIndex.get(i) * 3; + int quadIndex = i * 5; + quad.addData(quadIndex++, vertecies.get(index++) + offset.x()); // X + quad.addData(quadIndex++, vertecies.get(index++) + offset.y()); // Y + quad.addData(quadIndex++, vertecies.get(index) + offset.z()); // Z + if (hasUV) { + index = uvIndex.get(i) * 2; + quad.addData(quadIndex++, uvs.get(index++) * 16F); // U + quad.addData(quadIndex, (1 - uvs.get(index)) * 16F); // V + } + } + quad.setSpriteIndex(materialIndex); + if (useShading) { + Direction dir = getNormalDirection(quad); + quad.setDirection(dir); + quad.setShading(true); + } + if (useCulling) { + Direction dir = getCullingDirection(quad); + if (dir == null) { + quadsUnbaked.add(quad); + } else { + quadsUnbakedMap.get(dir).add(quad); + } + } else { + quadsUnbaked.add(quad); + } + } + } + + reader.close(); + streamReader.close(); + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + if (materialIndex < 0) { + quadsUnbaked.forEach(quad -> quad.setSpriteIndex(0)); + quadsUnbakedMap.values().forEach(list -> list.forEach(quad -> quad.setSpriteIndex(0))); + } + } + + private Direction getNormalDirection(UnbakedQuad quad) { + Vector3f pos = quad.getPos(0, POSITIONS[0]); + Vector3f dirA = quad.getPos(1, POSITIONS[1]); + Vector3f dirB = quad.getPos(2, POSITIONS[2]); + dirA.sub(pos); + dirB.sub(pos); + pos = MHelper.cross(dirA, dirB); + return Direction.getNearest(pos.x(), pos.y(), pos.z()); + } + + @Nullable + private Direction getCullingDirection(UnbakedQuad quad) { + Direction dir = null; + for (int i = 0; i < 4; i++) { + Vector3f pos = quad.getPos(i, POSITIONS[0]); + if (pos.x() < 1 && pos.x() > 0 && pos.y() < 1 && pos.y() > 0 && pos.z() < 1 && pos.z() > 0) { + return null; + } + Direction newDir = Direction.getNearest(pos.x() - 0.5F, pos.y() - 0.5F, pos.z() - 0.5F); + if (dir == null) { + dir = newDir; + } else if (newDir != dir) { + return null; + } + } + return dir; + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/OBJModelBuilder.java b/src/main/java/org/betterx/bclib/client/models/OBJModelBuilder.java new file mode 100644 index 00000000..29c80636 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/OBJModelBuilder.java @@ -0,0 +1,112 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Lists; +import com.mojang.math.Vector3f; + +import java.util.List; + +@Environment(EnvType.CLIENT) +public class OBJModelBuilder { + private static final OBJModelBuilder INSTANCE = new OBJModelBuilder(); + private final List textures = Lists.newArrayList(); + private final Vector3f offset = new Vector3f(); + private ResourceLocation modelLocation; + private ResourceLocation particles; + private boolean useCulling; + private boolean useShading; + + private OBJModelBuilder() { + } + + /** + * Start a new bodel building process, clears data of previous builder. + * + * @return {@link OBJModelBuilder} instance. + */ + public static OBJModelBuilder start(ResourceLocation modelLocation) { + INSTANCE.modelLocation = modelLocation; + INSTANCE.offset.set(0, 0, 0); + INSTANCE.useCulling = true; + INSTANCE.useShading = true; + INSTANCE.particles = null; + INSTANCE.textures.clear(); + return INSTANCE; + } + + /** + * Add texture to the model. All textures have indexes with same order as in source OBJ model. + * + * @param texture {@link ResourceLocation} texture ID. + * @return this {@link OBJModelBuilder}. + */ + public OBJModelBuilder addTexture(ResourceLocation texture) { + textures.add(texture); + return this; + } + + /** + * Culling used to remove block faces if they are on block faces or outside of the block to reduce faces count in rendering. + * Opaque blocks shoud have this as true to reduce geometry issues, block like plants should have this as false. + * Default value is {@code true}. + * + * @param useCulling {@link Boolean}. + * @return this {@link OBJModelBuilder}. + */ + public OBJModelBuilder useCulling(boolean useCulling) { + this.useCulling = useCulling; + return this; + } + + /** + * Shading tints block faces in shades of gray to immitate volume in MC rendering. + * Blocks like plants don't have shading, most full opaque blocks - have. + * Default value is {@code true}. + * + * @param useShading {@link Boolean}. + * @return this {@link OBJModelBuilder}. + */ + public OBJModelBuilder useShading(boolean useShading) { + this.useShading = useShading; + return this; + } + + /** + * Set particle texture for this model. + * Not required, if texture is not selected the first texture will be used instead of it. + * + * @param texture {@link ResourceLocation} texture ID. + * @return this {@link OBJModelBuilder}. + */ + public OBJModelBuilder setParticlesTexture(ResourceLocation texture) { + this.particles = texture; + return this; + } + + public OBJModelBuilder setOffset(float x, float y, float z) { + this.offset.set(x, y, z); + return this; + } + + /** + * Builds model from all required data. + * + * @return {@link OBJBlockModel}. + */ + public OBJBlockModel build() { + byte particleIndex = 0; + if (particles != null) { + particleIndex = (byte) textures.indexOf(particles); + if (particleIndex < 0) { + particleIndex = (byte) textures.size(); + textures.add(particles); + } + } + ResourceLocation[] sprites = textures.toArray(new ResourceLocation[textures.size()]); + return new OBJBlockModel(modelLocation, offset, useCulling, useShading, particleIndex, sprites); + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/PatternsHelper.java b/src/main/java/org/betterx/bclib/client/models/PatternsHelper.java new file mode 100644 index 00000000..0547df55 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/PatternsHelper.java @@ -0,0 +1,76 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; + +import com.google.common.collect.Maps; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public class PatternsHelper { + private static final Map JSON_CACHE = Maps.newConcurrentMap(); + + public static Optional createItemGenerated(ResourceLocation itemId) { + return createJson(BasePatterns.ITEM_GENERATED, itemId); + } + + public static Optional createItemHandheld(ResourceLocation itemId) { + return createJson(BasePatterns.ITEM_HANDHELD, itemId); + } + + public static Optional createBlockSimple(ResourceLocation blockId) { + return createJson(BasePatterns.BLOCK_BASE, blockId); + } + + public static Optional createBlockEmpty(ResourceLocation blockId) { + return createJson(BasePatterns.BLOCK_EMPTY, blockId); + } + + public static Optional createBlockPillar(ResourceLocation blockId) { + return createJson(BasePatterns.BLOCK_PILLAR, blockId); + } + + public static Optional createBlockBottomTop(ResourceLocation blockId) { + return createJson(BasePatterns.BLOCK_BOTTOM_TOP, blockId); + } + + public static Optional createBlockColored(ResourceLocation blockId) { + return createJson(BasePatterns.BLOCK_COLORED, blockId); + } + + public static Optional createJson(ResourceLocation patternId, ResourceLocation blockId) { + Map textures = Maps.newHashMap(); + textures.put("%modid%", blockId.getNamespace()); + textures.put("%texture%", blockId.getPath()); + return createJson(patternId, textures); + } + + public static Optional createJson(ResourceLocation patternId, Map textures) { + ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); + Optional patternRes = resourceManager.getResource(patternId); + if (patternRes.isEmpty()) return Optional.empty(); + + try (InputStream input = patternRes.get().open()) { + String json = JSON_CACHE.get(patternId); + if (json == null) { + json = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines() + .collect(Collectors.joining()); + JSON_CACHE.put(patternId, json); + } + for (Map.Entry texture : textures.entrySet()) { + json = json.replace(texture.getKey(), texture.getValue()); + } + return Optional.of(json); + } catch (Exception ex) { + return Optional.empty(); + } + } +} diff --git a/src/main/java/org/betterx/bclib/client/models/UnbakedQuad.java b/src/main/java/org/betterx/bclib/client/models/UnbakedQuad.java new file mode 100644 index 00000000..56c44bc5 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/models/UnbakedQuad.java @@ -0,0 +1,70 @@ +package org.betterx.bclib.client.models; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.core.Direction; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.mojang.math.Matrix4f; +import com.mojang.math.Vector3f; +import com.mojang.math.Vector4f; + +@Environment(EnvType.CLIENT) +public class UnbakedQuad { + private static final Vector4f POS = new Vector4f(); + private final float[] data = new float[20]; // 4 points with 3 positions and 2 uvs, 4 * (3 + 2) + private Direction dir = Direction.UP; + private boolean useShading = false; + private int spriteIndex; + + public void addData(int index, float value) { + data[index] = value; + } + + public void setSpriteIndex(int index) { + spriteIndex = index; + } + + public void setDirection(Direction dir) { + this.dir = dir; + } + + public void setShading(boolean useShading) { + this.useShading = useShading; + } + + public Vector3f getPos(int index, Vector3f result) { + int dataIndex = index * 5; + float x = data[dataIndex++]; + float y = data[dataIndex++]; + float z = data[dataIndex]; + result.set(x, y, z); + return result; + } + + public BakedQuad bake(TextureAtlasSprite[] sprites, ModelState modelState) { + Matrix4f matrix = modelState.getRotation().getMatrix(); + TextureAtlasSprite sprite = sprites[spriteIndex]; + int[] vertexData = new int[32]; + for (int i = 0; i < 4; i++) { + int index = i << 3; + int dataIndex = i * 5; + float x = data[dataIndex++]; // X + float y = data[dataIndex++]; // Y + float z = data[dataIndex++]; // Z + POS.set(x, y, z, 0); + POS.transform(matrix); + vertexData[index] = Float.floatToIntBits(POS.x()); // X + vertexData[index | 1] = Float.floatToIntBits(POS.y()); // Y + vertexData[index | 2] = Float.floatToIntBits(POS.z()); // Z + vertexData[index | 3] = -1; // Unknown constant + vertexData[index | 4] = Float.floatToIntBits(sprite.getU(data[dataIndex++])); // U + vertexData[index | 5] = Float.floatToIntBits(sprite.getV(data[dataIndex])); // V + } + // vertices, tint index, direction, sprite, shade + return new BakedQuad(vertexData, 0, dir, sprites[spriteIndex], useShading); + } +} diff --git a/src/main/java/org/betterx/bclib/client/render/BCLRenderLayer.java b/src/main/java/org/betterx/bclib/client/render/BCLRenderLayer.java new file mode 100644 index 00000000..8d21bb5b --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/render/BCLRenderLayer.java @@ -0,0 +1,5 @@ +package org.betterx.bclib.client.render; + +public enum BCLRenderLayer { + CUTOUT, TRANSLUCENT +} diff --git a/src/main/java/org/betterx/bclib/client/render/BaseChestBlockEntityRenderer.java b/src/main/java/org/betterx/bclib/client/render/BaseChestBlockEntityRenderer.java new file mode 100644 index 00000000..84fd78b2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/render/BaseChestBlockEntityRenderer.java @@ -0,0 +1,186 @@ +package org.betterx.bclib.client.render; + +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.BrightnessCombiner; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.DoubleBlockCombiner.NeighborCombineResult; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.ChestType; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Maps; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import it.unimi.dsi.fastutil.floats.Float2FloatFunction; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; +import org.betterx.bclib.blockentities.BaseChestBlockEntity; +import org.betterx.bclib.client.models.BaseChestBlockModel; + +import java.util.HashMap; + +@Environment(EnvType.CLIENT) +public class BaseChestBlockEntityRenderer implements BlockEntityRenderer { + private static final HashMap LAYERS = Maps.newHashMap(); + private static final RenderType[] RENDER_TYPES; + + private static final int ID_NORMAL = 0; + private static final int ID_LEFT = 1; + private static final int ID_RIGHT = 2; + + private final BaseChestBlockModel chestModel; + + public BaseChestBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { + super(); + chestModel = new BaseChestBlockModel(BaseChestBlockModel.getTexturedModelData().bakeRoot()); + } + + public void render(BaseChestBlockEntity entity, + float tickDelta, + PoseStack matrices, + MultiBufferSource vertexConsumers, + int light, + int overlay) { + Level world = entity.getLevel(); + boolean worldExists = world != null; + BlockState blockState = worldExists ? entity.getBlockState() : Blocks.CHEST.defaultBlockState() + .setValue( + ChestBlock.FACING, + Direction.SOUTH + ); + ChestType chestType = blockState.hasProperty(ChestBlock.TYPE) + ? blockState.getValue(ChestBlock.TYPE) + : ChestType.SINGLE; + Block block = blockState.getBlock(); + if (block instanceof AbstractChestBlock) { + AbstractChestBlock abstractChestBlock = (AbstractChestBlock) block; + boolean isDouble = chestType != ChestType.SINGLE; + float f = blockState.getValue(ChestBlock.FACING).toYRot(); + NeighborCombineResult propertySource; + + matrices.pushPose(); + matrices.translate(0.5D, 0.5D, 0.5D); + matrices.mulPose(Vector3f.YP.rotationDegrees(-f)); + matrices.translate(-0.5D, -0.5D, -0.5D); + + if (worldExists) { + propertySource = abstractChestBlock.combine(blockState, world, entity.getBlockPos(), true); + } else { + propertySource = DoubleBlockCombiner.Combiner::acceptNone; + } + + float pitch = propertySource.apply(ChestBlock.opennessCombiner(entity)).get( + tickDelta); + pitch = 1.0F - pitch; + pitch = 1.0F - pitch * pitch * pitch; + @SuppressWarnings({ + "unchecked", + "rawtypes" + }) int blockLight = ((Int2IntFunction) propertySource.apply(new BrightnessCombiner())).applyAsInt(light); + + VertexConsumer vertexConsumer = getConsumer(vertexConsumers, block, chestType); + + if (isDouble) { + if (chestType == ChestType.LEFT) { + renderParts( + matrices, + vertexConsumer, + chestModel.partLeftA, + chestModel.partLeftB, + chestModel.partLeftC, + pitch, + blockLight, + overlay + ); + } else { + renderParts( + matrices, + vertexConsumer, + chestModel.partRightA, + chestModel.partRightB, + chestModel.partRightC, + pitch, + blockLight, + overlay + ); + } + } else { + renderParts( + matrices, + vertexConsumer, + chestModel.partA, + chestModel.partB, + chestModel.partC, + pitch, + blockLight, + overlay + ); + } + + matrices.popPose(); + } + } + + private void renderParts(PoseStack matrices, + VertexConsumer vertices, + ModelPart modelPart, + ModelPart modelPart2, + ModelPart modelPart3, + float pitch, + int light, + int overlay) { + modelPart.xRot = -(pitch * 1.5707964F); + modelPart2.xRot = modelPart.xRot; + modelPart.render(matrices, vertices, light, overlay); + modelPart2.render(matrices, vertices, light, overlay); + modelPart3.render(matrices, vertices, light, overlay); + } + + private static RenderType getChestTexture(ChestType type, RenderType[] layers) { + return switch (type) { + case LEFT -> layers[ID_LEFT]; + case RIGHT -> layers[ID_RIGHT]; + default -> layers[ID_NORMAL]; + }; + } + + public static VertexConsumer getConsumer(MultiBufferSource provider, Block block, ChestType chestType) { + RenderType[] layers = LAYERS.getOrDefault(block, RENDER_TYPES); + return provider.getBuffer(getChestTexture(chestType, layers)); + } + + public static void registerRenderLayer(Block block) { + ResourceLocation blockId = Registry.BLOCK.getKey(block); + String modId = blockId.getNamespace(); + String path = blockId.getPath(); + LAYERS.put( + block, + new RenderType[]{ + RenderType.entityCutout(new ResourceLocation(modId, "textures/entity/chest/" + path + ".png")), + RenderType.entityCutout(new ResourceLocation(modId, + "textures/entity/chest/" + path + "_left.png")), + RenderType.entityCutout(new ResourceLocation(modId, + "textures/entity/chest/" + path + "_right.png")) + } + ); + } + + static { + RENDER_TYPES = new RenderType[]{ + RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal.png")), + RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal_left.png")), + RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal_right.png")) + }; + } +} diff --git a/src/main/java/org/betterx/bclib/client/render/BaseSignBlockEntityRenderer.java b/src/main/java/org/betterx/bclib/client/render/BaseSignBlockEntityRenderer.java new file mode 100644 index 00000000..8c885fac --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/render/BaseSignBlockEntityRenderer.java @@ -0,0 +1,190 @@ +package org.betterx.bclib.client.render; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.Sheets; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.SignRenderer; +import net.minecraft.client.resources.model.Material; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SignBlock; +import net.minecraft.world.level.block.StandingSignBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.WoodType; +import net.minecraft.world.phys.Vec3; + +import com.google.common.collect.Maps; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import org.betterx.bclib.blockentities.BaseSignBlockEntity; +import org.betterx.bclib.blocks.BaseSignBlock; + +import java.util.HashMap; +import java.util.List; + +public class BaseSignBlockEntityRenderer implements BlockEntityRenderer { + private static final HashMap RENDER_TYPES = Maps.newHashMap(); + private static final int OUTLINE_RENDER_DISTANCE = Mth.square(16); + private static final RenderType RENDER_TYPE; + private final SignRenderer.SignModel model; + private final Font font; + + + public BaseSignBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { + super(); + this.font = ctx.getFont(); + model = new SignRenderer.SignModel(ctx.bakeLayer(ModelLayers.createSignModelName(WoodType.OAK))); + } + + public void render(BaseSignBlockEntity signBlockEntity, + float tickDelta, + PoseStack matrixStack, + MultiBufferSource provider, + int light, + int overlay) { + BlockState state = signBlockEntity.getBlockState(); + + matrixStack.pushPose(); + + + matrixStack.translate(0.5D, 0.5D, 0.5D); + float angle = -((float) (state.getValue(StandingSignBlock.ROTATION) * 360) / 16.0F); + + BlockState blockState = signBlockEntity.getBlockState(); + if (blockState.getValue(BaseSignBlock.FLOOR)) { + matrixStack.mulPose(Vector3f.YP.rotationDegrees(angle)); + model.stick.visible = true; + } else { + matrixStack.mulPose(Vector3f.YP.rotationDegrees(angle + 180)); + matrixStack.translate(0.0D, -0.3125D, -0.4375D); + model.stick.visible = false; + } + + matrixStack.pushPose(); + matrixStack.scale(0.6666667F, -0.6666667F, -0.6666667F); + VertexConsumer vertexConsumer = getConsumer(provider, state.getBlock()); + + model.root.render(matrixStack, vertexConsumer, light, overlay); + matrixStack.popPose(); + matrixStack.translate(0.0D, 0.3333333432674408D, 0.046666666865348816D); + matrixStack.scale(0.010416667F, -0.010416667F, 0.010416667F); + int m = signBlockEntity.getColor().getTextColor(); + int n = (int) (NativeImage.getR(m) * 0.4D); + int o = (int) (NativeImage.getG(m) * 0.4D); + int p = (int) (NativeImage.getB(m) * 0.4D); + int q = NativeImage.combine(0, p, o, n); + + FormattedCharSequence[] formattedCharSequences = signBlockEntity.getRenderMessages( + Minecraft.getInstance() + .isTextFilteringEnabled(), + (component) -> { + List list = this.font.split(component, 90); + return list.isEmpty() ? FormattedCharSequence.EMPTY : list.get(0); + } + ); + int drawColor; + boolean drawOutlined; + int drawLight; + if (signBlockEntity.hasGlowingText()) { + drawColor = signBlockEntity.getColor().getTextColor(); + drawOutlined = isOutlineVisible(signBlockEntity, drawColor); + drawLight = 15728880; + } else { + drawColor = m; + drawOutlined = false; + drawLight = light; + } + + for (int s = 0; s < 4; ++s) { + FormattedCharSequence formattedCharSequence = formattedCharSequences[s]; + float t = (float) (-this.font.width(formattedCharSequence) / 2); + if (drawOutlined) { + this.font.drawInBatch8xOutline( + formattedCharSequence, + t, + (float) (s * 10 - 20), + drawColor, + m, + matrixStack.last().pose(), + provider, + drawLight + ); + } else { + this.font.drawInBatch( + formattedCharSequence, + t, + (float) (s * 10 - 20), + drawColor, + false, + matrixStack.last().pose(), + provider, + false, + 0, + drawLight + ); + } + } + + + matrixStack.popPose(); + } + + + private static boolean isOutlineVisible(BaseSignBlockEntity signBlockEntity, int i) { + if (i == DyeColor.BLACK.getTextColor()) { + return true; + } else { + Minecraft minecraft = Minecraft.getInstance(); + LocalPlayer localPlayer = minecraft.player; + if (localPlayer != null && minecraft.options.getCameraType().isFirstPerson() && localPlayer.isScoping()) { + return true; + } else { + Entity entity = minecraft.getCameraEntity(); + return entity != null && entity.distanceToSqr(Vec3.atCenterOf(signBlockEntity.getBlockPos())) < (double) OUTLINE_RENDER_DISTANCE; + } + } + } + + public static WoodType getSignType(Block block) { + WoodType signType2; + if (block instanceof SignBlock) { + signType2 = ((SignBlock) block).type(); + } else { + signType2 = WoodType.OAK; + } + + return signType2; + } + + public static Material getModelTexture(Block block) { + return Sheets.getSignMaterial(getSignType(block)); + } + + public static VertexConsumer getConsumer(MultiBufferSource provider, Block block) { + return provider.getBuffer(RENDER_TYPES.getOrDefault(block, RENDER_TYPE)); + } + + public static void registerRenderLayer(Block block) { + ResourceLocation blockId = Registry.BLOCK.getKey(block); + RenderType layer = RenderType.entitySolid(new ResourceLocation(blockId.getNamespace(), + "textures/entity/sign/" + blockId.getPath() + ".png")); + RENDER_TYPES.put(block, layer); + } + + static { + RENDER_TYPE = RenderType.entitySolid(new ResourceLocation("textures/entity/signs/oak.png")); + } +} diff --git a/src/main/java/org/betterx/bclib/client/render/CustomFogRenderer.java b/src/main/java/org/betterx/bclib/client/render/CustomFogRenderer.java new file mode 100644 index 00000000..9d4b5734 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/render/CustomFogRenderer.java @@ -0,0 +1,155 @@ +package org.betterx.bclib.client.render; + +import net.minecraft.client.Camera; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.material.FogType; + +import com.mojang.blaze3d.systems.RenderSystem; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.util.BackgroundInfo; +import org.betterx.bclib.util.MHelper; +import org.betterx.bclib.world.biomes.BCLBiome; + +public class CustomFogRenderer { + private static final MutableBlockPos LAST_POS = new MutableBlockPos(0, -100, 0); + 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, float viewDistance, boolean thickFog) { + if (!Configs.CLIENT_CONFIG.renderCustomFog()) { + return false; + } + + FogType fogType = camera.getFluidInCamera(); + if (fogType != FogType.NONE) { + 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(thickFog, entity.level)) { + fogStart = viewDistance * 0.05F / fog; + fogEnd = Math.min(viewDistance, 192.0F) * 0.5F / fog; + } else { + fogStart = viewDistance * 0.25F / fog; // In vanilla - 0 + 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 { + float delta = (float) duration / 20F; + BackgroundInfo.blindness = delta; + fogStart = Mth.lerp(delta, fogStart, 0); + fogEnd = Mth.lerp(delta, fogEnd, fogEnd * 0.03F); + } + } else { + BackgroundInfo.blindness = 0; + } + } + + RenderSystem.setShaderFogStart(fogStart); + RenderSystem.setShaderFogEnd(fogEnd); + + return true; + } + + private static boolean thickFog(boolean thickFog, Level level) { + if (!thickFog) { + return false; + } + if (level.dimension() == Level.NETHER) { + return Configs.CLIENT_CONFIG.netherThickFog(); + } + 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) { + Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value(); + return BiomeAPI.getRenderBiome(biome) == BiomeAPI.EMPTY_BIOME; + } + + private static float getFogDensityI(Level level, int x, int y, int z) { + Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value(); + BCLBiome renderBiome = BiomeAPI.getRenderBiome(biome); + return renderBiome.getFogDensity(); + } + + private static float getFogDensity(Level level, double x, double y, double z) { + int x1 = MHelper.floor(x / GRID_SIZE) * GRID_SIZE; + int y1 = MHelper.floor(y / GRID_SIZE) * GRID_SIZE; + int z1 = MHelper.floor(z / GRID_SIZE) * GRID_SIZE; + float dx = (float) (x - x1) / GRID_SIZE; + float dy = (float) (y - y1) / GRID_SIZE; + float dz = (float) (z - z1) / GRID_SIZE; + + if (LAST_POS.getX() != x1 || LAST_POS.getY() != y1 || LAST_POS.getZ() != z1) { + int x2 = x1 + GRID_SIZE; + int y2 = y1 + GRID_SIZE; + int z2 = z1 + GRID_SIZE; + LAST_POS.set(x1, y1, z1); + FOG_DENSITY[0] = getFogDensityI(level, x1, y1, z1); + FOG_DENSITY[1] = getFogDensityI(level, x2, y1, z1); + FOG_DENSITY[2] = getFogDensityI(level, x1, y2, z1); + FOG_DENSITY[3] = getFogDensityI(level, x2, y2, z1); + FOG_DENSITY[4] = getFogDensityI(level, x1, y1, z2); + FOG_DENSITY[5] = getFogDensityI(level, x2, y1, z2); + FOG_DENSITY[6] = getFogDensityI(level, x1, y2, z2); + FOG_DENSITY[7] = getFogDensityI(level, x2, y2, z2); + } + + float a = Mth.lerp(dx, FOG_DENSITY[0], FOG_DENSITY[1]); + float b = Mth.lerp(dx, FOG_DENSITY[2], FOG_DENSITY[3]); + float c = Mth.lerp(dx, FOG_DENSITY[4], FOG_DENSITY[5]); + float d = Mth.lerp(dx, FOG_DENSITY[6], FOG_DENSITY[7]); + + a = Mth.lerp(dy, a, b); + b = Mth.lerp(dy, c, d); + + return Mth.lerp(dz, a, b); + } +} diff --git a/src/main/java/org/betterx/bclib/client/render/EmissiveTextureInfo.java b/src/main/java/org/betterx/bclib/client/render/EmissiveTextureInfo.java new file mode 100644 index 00000000..e2acddae --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/render/EmissiveTextureInfo.java @@ -0,0 +1,33 @@ +package org.betterx.bclib.client.render; + +import net.minecraft.resources.ResourceLocation; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class EmissiveTextureInfo { + private static final Set EMISSIVE_TEXTURES = Sets.newHashSet(); + private static final Set EMISSIVE_BLOCKS = Sets.newHashSet(); + + public static void clear() { + EMISSIVE_TEXTURES.clear(); + EMISSIVE_BLOCKS.clear(); + } + + public static void addTexture(ResourceLocation texture) { + EMISSIVE_TEXTURES.add(texture); + } + + public static void addBlock(ResourceLocation blockID) { + EMISSIVE_BLOCKS.add(blockID); + } + + public static boolean isEmissiveTexture(ResourceLocation texture) { + return EMISSIVE_TEXTURES.contains(texture); + } + + public static boolean isEmissiveBlock(ResourceLocation blockID) { + return EMISSIVE_BLOCKS.contains(blockID); + } +} diff --git a/src/main/java/org/betterx/bclib/client/sound/BlockSounds.java b/src/main/java/org/betterx/bclib/client/sound/BlockSounds.java new file mode 100644 index 00000000..fd4f6b80 --- /dev/null +++ b/src/main/java/org/betterx/bclib/client/sound/BlockSounds.java @@ -0,0 +1,16 @@ +package org.betterx.bclib.client.sound; + +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.SoundType; + +public class BlockSounds { + public static final SoundType TERRAIN_SOUND = new SoundType( + 1.0F, + 1.0F, + SoundEvents.STONE_BREAK, + SoundEvents.WART_BLOCK_STEP, + SoundEvents.STONE_PLACE, + SoundEvents.STONE_HIT, + SoundEvents.STONE_FALL + ); +} diff --git a/src/main/java/org/betterx/bclib/complexmaterials/ComplexMaterial.java b/src/main/java/org/betterx/bclib/complexmaterials/ComplexMaterial.java new file mode 100644 index 00000000..e0b15a79 --- /dev/null +++ b/src/main/java/org/betterx/bclib/complexmaterials/ComplexMaterial.java @@ -0,0 +1,370 @@ +package org.betterx.bclib.complexmaterials; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.complexmaterials.entry.BlockEntry; +import org.betterx.bclib.complexmaterials.entry.ItemEntry; +import org.betterx.bclib.complexmaterials.entry.RecipeEntry; +import org.betterx.bclib.config.PathConfig; +import org.betterx.bclib.registry.BlockRegistry; +import org.betterx.bclib.registry.ItemRegistry; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.Nullable; + +public abstract class ComplexMaterial { + private static final Map> RECIPE_ENTRIES = Maps.newHashMap(); + private static final Map> BLOCK_ENTRIES = Maps.newHashMap(); + private static final Map> ITEM_ENTRIES = Maps.newHashMap(); + private static final List MATERIALS = Lists.newArrayList(); + + private final List defaultRecipeEntries = Lists.newArrayList(); + private final List defaultBlockEntries = Lists.newArrayList(); + private final List defaultItemEntries = Lists.newArrayList(); + + private final Map> blockTags = Maps.newHashMap(); + private final Map> itemTags = Maps.newHashMap(); + private final Map blocks = Maps.newHashMap(); + private final Map items = Maps.newHashMap(); + + protected final String baseName; + protected final String modID; + protected final String receipGroupPrefix; + + public ComplexMaterial(String modID, String baseName, String receipGroupPrefix) { + this.baseName = baseName; + this.modID = modID; + this.receipGroupPrefix = receipGroupPrefix; + MATERIALS.add(this); + } + + /** + * Initialize and registers all content inside material, return material itself. + * + * @param blocksRegistry {@link BlockRegistry} instance to add blocks in; + * @param itemsRegistry {@link ItemRegistry} instance to add items in; + * @param recipeConfig {@link PathConfig} for recipes check. + * @return {@link ComplexMaterial}. + */ + public ComplexMaterial init(BlockRegistry blocksRegistry, ItemRegistry itemsRegistry, PathConfig recipeConfig) { + initTags(); + + final FabricBlockSettings blockSettings = getBlockSettings(); + final FabricItemSettings itemSettings = getItemSettings(itemsRegistry); + initDefault(blockSettings, itemSettings); + + getBlockEntries().forEach(entry -> { + Block block = entry.init(this, blockSettings, blocksRegistry); + blocks.put(entry.getSuffix(), block); + }); + + getItemEntries().forEach(entry -> { + Item item = entry.init(this, itemSettings, itemsRegistry); + items.put(entry.getSuffix(), item); + }); + + initDefaultRecipes(); + getRecipeEntries().forEach(entry -> { + entry.init(this, recipeConfig); + }); + + initFlammable(FlammableBlockRegistry.getDefaultInstance()); + return this; + } + + /** + * Init default content for {@link ComplexMaterial} - blocks and items. + * + * @param blockSettings {@link FabricBlockSettings} default block settings for this material; + * @param itemSettings {@link FabricItemSettings} default item settings for this material. + */ + protected abstract void initDefault(FabricBlockSettings blockSettings, FabricItemSettings itemSettings); + + /** + * Init custom tags for this {@link ComplexMaterial}, not required. + */ + protected void initTags() { + } + + /** + * Init default recipes for this {@link ComplexMaterial}, not required. + */ + protected void initDefaultRecipes() { + } + + /** + * Allows to add blocks into Fabric {@link FlammableBlockRegistry} for this {@link ComplexMaterial}, not required. + */ + protected void initFlammable(FlammableBlockRegistry registry) { + } + + /** + * Adds custom block tag for this {@link ComplexMaterial}, tag can be created with {@link TagAPI} or you can use one of already created tags. + * + * @param tag {@link TagKey} for {@link Block} + */ + protected void addBlockTag(TagKey tag) { + String key = tag.location().getPath().replace(getBaseName() + "_", ""); + blockTags.put(key, tag); + } + + /** + * Adds custom item tag for this {@link ComplexMaterial}, tag can be created with {@link TagAPI} or you can use one of already created tags. + * + * @param tag {@link TagKey} for {@link Item} + */ + protected void addItemTag(TagKey tag) { + String key = tag.location().getPath().replace(getBaseName() + "_", ""); + itemTags.put(key, tag); + } + + /** + * Get custom {@link Block} {@link TagKey} from this {@link ComplexMaterial}. + * + * @param key {@link String} tag name (path of its {@link ResourceLocation}), for inner tags created inside material its tag suffix. + * @return {@link TagKey} for {@link Block} or {@code null} if nothing is stored. + */ + @Nullable + public TagKey getBlockTag(String key) { + return blockTags.get(key); + } + + /** + * Get custom {@link Item} {@link TagKey} from this {@link ComplexMaterial}. + * + * @param key {@link String} tag name (path of its {@link ResourceLocation}), for inner tags created inside material its tag suffix. + * @return {@link TagKey} for {@link Item} or {@code null} if nothing is stored. + */ + @Nullable + public TagKey getItemTag(String key) { + return itemTags.get(key); + } + + /** + * Get initiated {@link Block} from this {@link ComplexMaterial}. + * + * @param key {@link String} block name suffix (example: "mod:custom_log" will have a "log" suffix if "custom" is a base name of this material) + * @return {@link Block} or {@code null} if nothing is stored. + */ + @Nullable + public Block getBlock(String key) { + return blocks.get(key); + } + + /** + * Get initiated {@link Item} from this {@link ComplexMaterial}. + * + * @param key {@link String} block name suffix (example: "mod:custom_apple" will have a "apple" suffix if "custom" is a base name of this material) + * @return {@link Item} or {@code null} if nothing is stored. + */ + @Nullable + public Item getItem(String key) { + return items.get(key); + } + + /** + * Get default block settings for this material. + * + * @return {@link FabricBlockSettings} + */ + protected abstract FabricBlockSettings getBlockSettings(); + + /** + * Get default item settings for this material. + * + * @return {@link FabricItemSettings} + */ + protected FabricItemSettings getItemSettings(ItemRegistry registry) { + return registry.makeItemSettings(); + } + + private Collection getBlockEntries() { + List result = Lists.newArrayList(defaultBlockEntries); + List entries = BLOCK_ENTRIES.get(this.getMaterialID()); + if (entries != null) { + result.addAll(entries); + } + return result; + } + + private Collection getItemEntries() { + List result = Lists.newArrayList(defaultItemEntries); + List entries = ITEM_ENTRIES.get(this.getMaterialID()); + if (entries != null) { + result.addAll(entries); + } + return result; + } + + private Collection getRecipeEntries() { + List result = Lists.newArrayList(defaultRecipeEntries); + List entries = RECIPE_ENTRIES.get(this.getMaterialID()); + if (entries != null) { + result.addAll(entries); + } + return result; + } + + /** + * Get base name of this {@link ComplexMaterial}. + * + * @return {@link String} name + */ + public String getBaseName() { + return baseName; + } + + /** + * Get mod ID for this {@link ComplexMaterial}. + * + * @return {@link String} mod ID. + */ + public String getModID() { + return modID; + } + + /** + * Get a unique {@link ResourceLocation} for each material class. + * For example WoodenComplexMaterial will have a "bclib:Wooden_Complex_Material" {@link ResourceLocation}. + * This is used to add custom entries before mods init using Fabric "preLaunch" entry point. + * + * @return {@link ResourceLocation} for this material + * @see Fabric Documentation: Entrypoint + */ + public abstract ResourceLocation getMaterialID(); + + /** + * Get all initiated block from this {@link ComplexMaterial}. + * + * @return {@link Collection} of {@link Block}. + */ + public Collection getBlocks() { + return blocks.values(); + } + + /** + * Get all initiated items from this {@link ComplexMaterial}. + * + * @return {@link Collection} of {@link Item}. + */ + public Collection getItems() { + return items.values(); + } + + /** + * Adds a default {@link BlockEntry} to this {@link ComplexMaterial}. Used to initiate blocks later. + * + * @param entry {@link BlockEntry} + */ + protected void addBlockEntry(BlockEntry entry) { + defaultBlockEntries.add(entry); + } + + /** + * Replaces or Adds a default {@link BlockEntry} to this {@link ComplexMaterial}. Used to initiate blocks later. + *

+ * If this {@link ComplexMaterial} does already contain an entry for the {@link ResourceLocation}, the entry will + * be removed first. + * + * @param entry {@link BlockEntry} + */ + protected void replaceOrAddBlockEntry(BlockEntry entry) { + int pos = defaultBlockEntries.indexOf(entry); + if (pos >= 0) defaultBlockEntries.remove(entry); + + addBlockEntry(entry); + } + + /** + * Adds a default {@link ItemEntry} to this {@link ComplexMaterial}. Used to initiate items later. + * + * @param entry {@link ItemEntry} + */ + protected void addItemEntry(ItemEntry entry) { + defaultItemEntries.add(entry); + } + + /** + * Adds a default {@link RecipeEntry} to this {@link ComplexMaterial}. Used to initiate items later. + * + * @param entry {@link RecipeEntry} + */ + protected void addRecipeEntry(RecipeEntry entry) { + defaultRecipeEntries.add(entry); + } + + /** + * Adds a custom {@link BlockEntry} for specified {@link ComplexMaterial} using its {@link ResourceLocation}. + * Used to add custom entry for all instances of {@link ComplexMaterial}. + * Should be called only using Fabric "preLaunch" entry point. + * + * @param materialName {@link ResourceLocation} id of {@link ComplexMaterial}; + * @param entry {@link BlockEntry}. + * @see Fabric Documentation: Entrypoint + */ + public static void addBlockEntry(ResourceLocation materialName, BlockEntry entry) { + List entries = BLOCK_ENTRIES.get(materialName); + if (entries == null) { + entries = Lists.newArrayList(); + BLOCK_ENTRIES.put(materialName, entries); + } + entries.add(entry); + } + + /** + * Adds a custom {@link ItemEntry} for specified {@link ComplexMaterial} using its {@link ResourceLocation}. + * Used to add custom entry for all instances of {@link ComplexMaterial}. + * Should be called only using Fabric "preLaunch" entry point. + * + * @param materialName {@link ResourceLocation} id of {@link ComplexMaterial}; + * @param entry {@link ItemEntry}. + * @see Fabric Documentation: Entrypoint + */ + public static void addItemEntry(ResourceLocation materialName, ItemEntry entry) { + List entries = ITEM_ENTRIES.get(materialName); + if (entries == null) { + entries = Lists.newArrayList(); + ITEM_ENTRIES.put(materialName, entries); + } + entries.add(entry); + } + + /** + * Adds a custom {@link RecipeEntry} for specified {@link ComplexMaterial} using its {@link ResourceLocation}. + * Used to add custom entry for all instances of {@link ComplexMaterial}. + * Should be called only using Fabric "preLaunch" entry point. + * + * @param materialName {@link ResourceLocation} id of {@link ComplexMaterial}; + * @param entry {@link RecipeEntry}. + * @see Fabric Documentation: Entrypoint + */ + public static void addRecipeEntry(ResourceLocation materialName, RecipeEntry entry) { + List entries = RECIPE_ENTRIES.get(materialName); + if (entries == null) { + entries = Lists.newArrayList(); + RECIPE_ENTRIES.put(materialName, entries); + } + entries.add(entry); + } + + /** + * Get all instances of all materials. + * + * @return {@link Collection} of {@link ComplexMaterial}. + */ + public static Collection getAllMaterials() { + return MATERIALS; + } +} diff --git a/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java b/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java new file mode 100644 index 00000000..c1003f94 --- /dev/null +++ b/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java @@ -0,0 +1,386 @@ +package org.betterx.bclib.complexmaterials; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.material.MaterialColor; + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.tag.*; +import org.betterx.bclib.blocks.*; +import org.betterx.bclib.complexmaterials.entry.BlockEntry; +import org.betterx.bclib.complexmaterials.entry.RecipeEntry; +import org.betterx.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) + .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) { + TagKey tagBlockLog = getBlockTag(TAG_LOGS); + TagKey tagItemLog = getItemTag(TAG_LOGS); + + addBlockEntry( + new BlockEntry(BLOCK_STRIPPED_LOG, (complexMaterial, settings) -> new BaseRotatedPillarBlock(settings)) + .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) + .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) + ); + addBlockEntry( + new BlockEntry(BLOCK_STRIPPED_BARK, (complexMaterial, settings) -> new BaseBarkBlock(settings)) + .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) + .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) + ); + + addBlockEntry( + new BlockEntry(BLOCK_LOG, + (complexMaterial, settings) -> new BaseStripableLogBlock(woodColor, + getBlock(BLOCK_STRIPPED_LOG))) + .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) + .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) + ); + addBlockEntry( + new BlockEntry(BLOCK_BARK, + (complexMaterial, settings) -> new StripableBarkBlock(woodColor, + getBlock(BLOCK_STRIPPED_BARK))) + .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) + .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) + ); + addBlockEntry(new BlockEntry(BLOCK_PLANKS, (complexMaterial, settings) -> new BaseBlock(settings)) + .setBlockTags(NamedBlockTags.PLANKS) + .setItemTags(NamedItemTags.PLANKS)); + + addBlockEntry(new BlockEntry(BLOCK_STAIRS, + (complexMaterial, settings) -> new BaseStairsBlock(getBlock(BLOCK_PLANKS), false)) + .setBlockTags(NamedBlockTags.WOODEN_STAIRS, NamedBlockTags.STAIRS) + .setItemTags(NamedItemTags.WOODEN_STAIRS, NamedItemTags.STAIRS)); + + addBlockEntry(new BlockEntry(BLOCK_SLAB, + (complexMaterial, settings) -> new BaseSlabBlock(getBlock(BLOCK_PLANKS), false)) + .setBlockTags(NamedBlockTags.WOODEN_SLABS, NamedBlockTags.SLABS) + .setItemTags(NamedItemTags.WOODEN_SLABS, NamedItemTags.SLABS)); + + addBlockEntry(new BlockEntry(BLOCK_FENCE, + (complexMaterial, settings) -> new BaseFenceBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.FENCES, NamedBlockTags.WOODEN_FENCES) + .setItemTags(NamedItemTags.FENCES, NamedItemTags.WOODEN_FENCES)); + + addBlockEntry(new BlockEntry(BLOCK_GATE, + (complexMaterial, settings) -> new BaseGateBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.FENCE_GATES)); + + addBlockEntry(new BlockEntry(BLOCK_BUTTON, + (complexMaterial, settings) -> new BaseWoodenButtonBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.BUTTONS, NamedBlockTags.WOODEN_BUTTONS) + .setItemTags(NamedItemTags.BUTTONS, NamedItemTags.WOODEN_BUTTONS)); + + addBlockEntry(new BlockEntry(BLOCK_PRESSURE_PLATE, + (complexMaterial, settings) -> new WoodenPressurePlateBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.PRESSURE_PLATES, NamedBlockTags.WOODEN_PRESSURE_PLATES) + .setItemTags(NamedItemTags.WOODEN_PRESSURE_PLATES)); + + addBlockEntry(new BlockEntry(BLOCK_TRAPDOOR, + (complexMaterial, settings) -> new BaseTrapdoorBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.TRAPDOORS, NamedBlockTags.WOODEN_TRAPDOORS) + .setItemTags(NamedItemTags.TRAPDOORS, NamedItemTags.WOODEN_TRAPDOORS)); + + addBlockEntry(new BlockEntry(BLOCK_DOOR, + (complexMaterial, settings) -> new BaseDoorBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.DOORS, NamedBlockTags.WOODEN_DOORS) + .setItemTags(NamedItemTags.DOORS, NamedItemTags.WOODEN_DOORS)); + + addBlockEntry(new BlockEntry(BLOCK_LADDER, + (complexMaterial, settings) -> new BaseLadderBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.CLIMBABLE)); + + addBlockEntry(new BlockEntry(BLOCK_SIGN, + (complexMaterial, settings) -> new BaseSignBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(NamedBlockTags.SIGNS) + .setItemTags(NamedItemTags.SIGNS)); + } + + final protected void initStorage(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) { + addBlockEntry(new BlockEntry(BLOCK_CHEST, + (complexMaterial, settings) -> new BaseChestBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(CommonBlockTags.CHEST, CommonBlockTags.WOODEN_CHEST) + .setItemTags(CommonItemTags.CHEST, CommonItemTags.WOODEN_CHEST)); + + addBlockEntry(new BlockEntry(BLOCK_BARREL, + (complexMaterial, settings) -> new BaseBarrelBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(CommonBlockTags.BARREL, CommonBlockTags.WOODEN_BARREL) + .setItemTags(CommonItemTags.BARREL, CommonItemTags.WOODEN_BARREL)); + } + + final protected void initDecorations(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) { + addBlockEntry(new BlockEntry(BLOCK_CRAFTING_TABLE, + (complexMaterial, settings) -> new BaseCraftingTableBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(CommonBlockTags.WORKBENCHES) + .setItemTags(CommonItemTags.WORKBENCHES)); + + addBlockEntry(new BlockEntry(BLOCK_BOOKSHELF, + (complexMaterial, settings) -> new BaseBookshelfBlock(getBlock(BLOCK_PLANKS))) + .setBlockTags(CommonBlockTags.BOOKSHELVES)); + + addBlockEntry(new BlockEntry(BLOCK_COMPOSTER, + (complexMaterial, settings) -> 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(); + })); + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/complexmaterials/entry/BlockEntry.java b/src/main/java/org/betterx/bclib/complexmaterials/entry/BlockEntry.java new file mode 100644 index 00000000..02bbd8e9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/complexmaterials/entry/BlockEntry.java @@ -0,0 +1,61 @@ +package org.betterx.bclib.complexmaterials.entry; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; + +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.complexmaterials.ComplexMaterial; +import org.betterx.bclib.registry.BlockRegistry; + +import java.util.function.BiFunction; + +public class BlockEntry extends ComplexMaterialEntry { + final BiFunction initFunction; + final boolean hasItem; + + TagKey[] blockTags; + TagKey[] itemTags; + + public BlockEntry(String suffix, BiFunction initFunction) { + this(suffix, true, initFunction); + } + + public BlockEntry(String suffix, + boolean hasItem, + BiFunction initFunction) { + super(suffix); + this.initFunction = initFunction; + this.hasItem = hasItem; + } + + public BlockEntry setBlockTags(TagKey... blockTags) { + this.blockTags = blockTags; + return this; + } + + public BlockEntry setItemTags(TagKey... itemTags) { + this.itemTags = itemTags; + return this; + } + + public Block init(ComplexMaterial material, FabricBlockSettings blockSettings, BlockRegistry registry) { + ResourceLocation location = getLocation(material.getModID(), material.getBaseName()); + Block block = initFunction.apply(material, blockSettings); + if (hasItem) { + registry.register(location, block); + if (itemTags != null) { + TagAPI.addItemTags(block, itemTags); + } + } else { + registry.registerBlockOnly(location, block); + } + if (blockTags != null) { + TagAPI.addBlockTags(block, blockTags); + } + return block; + } +} diff --git a/src/main/java/org/betterx/bclib/complexmaterials/entry/ComplexMaterialEntry.java b/src/main/java/org/betterx/bclib/complexmaterials/entry/ComplexMaterialEntry.java new file mode 100644 index 00000000..9dea2b60 --- /dev/null +++ b/src/main/java/org/betterx/bclib/complexmaterials/entry/ComplexMaterialEntry.java @@ -0,0 +1,40 @@ +package org.betterx.bclib.complexmaterials.entry; + +import net.minecraft.resources.ResourceLocation; + +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +public abstract class ComplexMaterialEntry { + @NotNull + private final String suffix; + + protected ComplexMaterialEntry(String suffix) { + this.suffix = suffix; + } + + public String getName(String baseName) { + return baseName + "_" + suffix; + } + + public ResourceLocation getLocation(String modID, String baseName) { + return new ResourceLocation(modID, getName(baseName)); + } + + public String getSuffix() { + return suffix; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ComplexMaterialEntry that = (ComplexMaterialEntry) o; + return suffix.equals(that.suffix); + } + + @Override + public int hashCode() { + return Objects.hash(suffix); + } +} diff --git a/src/main/java/org/betterx/bclib/complexmaterials/entry/ItemEntry.java b/src/main/java/org/betterx/bclib/complexmaterials/entry/ItemEntry.java new file mode 100644 index 00000000..37f34d50 --- /dev/null +++ b/src/main/java/org/betterx/bclib/complexmaterials/entry/ItemEntry.java @@ -0,0 +1,39 @@ +package org.betterx.bclib.complexmaterials.entry; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; + +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.complexmaterials.ComplexMaterial; +import org.betterx.bclib.registry.ItemRegistry; + +import java.util.function.BiFunction; + +public class ItemEntry extends ComplexMaterialEntry { + final BiFunction initFunction; + + TagKey[] itemTags; + + public ItemEntry(String suffix, BiFunction initFunction) { + super(suffix); + this.initFunction = initFunction; + } + + public ItemEntry setItemTags(TagKey[] itemTags) { + this.itemTags = itemTags; + return this; + } + + public Item init(ComplexMaterial material, FabricItemSettings itemSettings, ItemRegistry registry) { + ResourceLocation location = getLocation(material.getModID(), material.getBaseName()); + Item item = initFunction.apply(material, itemSettings); + registry.register(location, item); + if (itemTags != null) { + TagAPI.addItemTags(item, itemTags); + } + return item; + } +} diff --git a/src/main/java/org/betterx/bclib/complexmaterials/entry/RecipeEntry.java b/src/main/java/org/betterx/bclib/complexmaterials/entry/RecipeEntry.java new file mode 100644 index 00000000..a6dc15e2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/complexmaterials/entry/RecipeEntry.java @@ -0,0 +1,20 @@ +package org.betterx.bclib.complexmaterials.entry; + +import net.minecraft.resources.ResourceLocation; + +import org.betterx.bclib.complexmaterials.ComplexMaterial; +import org.betterx.bclib.config.PathConfig; +import org.betterx.bclib.interfaces.TriConsumer; + +public class RecipeEntry extends ComplexMaterialEntry { + final TriConsumer initFunction; + + public RecipeEntry(String suffix, TriConsumer initFunction) { + super(suffix); + this.initFunction = initFunction; + } + + public void init(ComplexMaterial material, PathConfig recipeConfig) { + initFunction.accept(material, recipeConfig, getLocation(material.getModID(), material.getBaseName())); + } +} diff --git a/src/main/java/org/betterx/bclib/config/CategoryConfig.java b/src/main/java/org/betterx/bclib/config/CategoryConfig.java new file mode 100644 index 00000000..5fe67b2b --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/CategoryConfig.java @@ -0,0 +1,10 @@ +package org.betterx.bclib.config; + +public class CategoryConfig extends IdConfig { + + public CategoryConfig(String modID, String group) { + super(modID, group, (id, category) -> { + return new ConfigKey(id.getPath(), id.getNamespace(), category); + }); + } +} diff --git a/src/main/java/org/betterx/bclib/config/ClientConfig.java b/src/main/java/org/betterx/bclib/config/ClientConfig.java new file mode 100644 index 00000000..5e16a52a --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/ClientConfig.java @@ -0,0 +1,92 @@ +package org.betterx.bclib.config; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSync; + +public class ClientConfig extends NamedPathConfig { + public static final ConfigToken SUPPRESS_EXPERIMENTAL_DIALOG = ConfigToken.Boolean(false, + "suppressExperimentalDialogOnLoad", + "ui"); + + @ConfigUI(topPadding = 12) + public static final ConfigToken ENABLED = ConfigToken.Boolean(true, "enabled", AutoSync.SYNC_CATEGORY); + + @ConfigUI(leftPadding = 8) + public static final DependendConfigToken ACCEPT_CONFIGS = DependendConfigToken.Boolean(true, + "acceptConfigs", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + @ConfigUI(leftPadding = 8) + public static final DependendConfigToken ACCEPT_FILES = DependendConfigToken.Boolean(true, + "acceptFiles", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + @ConfigUI(leftPadding = 8) + public static final DependendConfigToken ACCEPT_MODS = DependendConfigToken.Boolean(false, + "acceptMods", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + @ConfigUI(leftPadding = 8) + public static final DependendConfigToken DISPLAY_MOD_INFO = DependendConfigToken.Boolean(true, + "displayModInfo", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + + @ConfigUI(topPadding = 12) + public static final ConfigToken DEBUG_HASHES = ConfigToken.Boolean(false, + "debugHashes", + AutoSync.SYNC_CATEGORY); + + @ConfigUI(leftPadding = 8) + public static final ConfigToken CUSTOM_FOG_RENDERING = ConfigToken.Boolean(true, + "customFogRendering", + "rendering"); + @ConfigUI(leftPadding = 8) + public static final ConfigToken NETHER_THICK_FOG = ConfigToken.Boolean(true, + "netherThickFog", + "rendering"); + + public ClientConfig() { + super(BCLib.MOD_ID, "client", false); + } + + public boolean shouldPrintDebugHashes() { + return get(DEBUG_HASHES); + } + + public boolean isAllowingAutoSync() { + return get(ENABLED); + } + + public boolean isAcceptingMods() { + return get(ACCEPT_MODS) /*&& isAllowingAutoSync()*/; + } + + public boolean isAcceptingConfigs() { + return get(ACCEPT_CONFIGS) /*&& isAllowingAutoSync()*/; + } + + public boolean isAcceptingFiles() { + return get(ACCEPT_FILES) /*&& isAllowingAutoSync()*/; + } + + public boolean isShowingModInfo() { + return get(DISPLAY_MOD_INFO) /*&& isAllowingAutoSync()*/; + } + + public boolean suppressExperimentalDialog() { + return get(SUPPRESS_EXPERIMENTAL_DIALOG); + } + + public boolean netherThickFog() { + return get(NETHER_THICK_FOG); + } + + public boolean renderCustomFog() { + return get(CUSTOM_FOG_RENDERING); + } +} diff --git a/src/main/java/org/betterx/bclib/config/Config.java b/src/main/java/org/betterx/bclib/config/Config.java new file mode 100644 index 00000000..ebb83c5f --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/Config.java @@ -0,0 +1,234 @@ +package org.betterx.bclib.config; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.api.dataexchange.SyncFileHash; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSyncID; +import org.betterx.bclib.api.dataexchange.handler.autosync.FileContentWrapper; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.Nullable; + +public abstract class Config { + protected final static Map AUTO_SYNC_CONFIGS = new HashMap<>(); + public static final String CONFIG_SYNC_PREFIX = "CONFIG_"; + protected final ConfigKeeper keeper; + protected final boolean autoSync; + public final String configID; + + protected abstract void registerEntries(); + + protected Config(String modID, String group) { + this(modID, group, true, false); + } + + protected Config(String modID, String group, boolean autoSync) { + this(modID, group, autoSync, false); + } + + protected Config(String modID, String group, boolean autoSync, boolean diffContent) { + configID = modID + "." + group; + this.keeper = new ConfigKeeper(modID, group); + this.registerEntries(); + this.autoSync = autoSync; + + if (autoSync) { + final String uid = CONFIG_SYNC_PREFIX + configID; + final AutoSyncID aid = new AutoSyncID(BCLib.MOD_ID, uid); + if (diffContent) + DataExchangeAPI.addAutoSyncFile(aid.modID, aid.uniqueID, keeper.getConfigFile(), this::compareForSync); + else + DataExchangeAPI.addAutoSyncFile(aid.modID, aid.uniqueID, keeper.getConfigFile()); + + AUTO_SYNC_CONFIGS.put(aid, this); + BCLib.LOGGER.info("Added Config " + configID + " to auto sync (" + (diffContent + ? "content diff" + : "file hash") + ")"); + } + } + + private boolean compareForSync(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content) { + //identical hashes => nothing to do + if (clientHash.equals(serverHash)) { + return false; + } + + return keeper.compareAndUpdateForSync(content); + } + + public void saveChanges() { + this.keeper.save(); + } + + public static void reloadSyncedConfig(AutoSyncID aid, File file) { + Config cfg = AUTO_SYNC_CONFIGS.get(aid); + if (cfg != null) { + cfg.reload(); + } + } + + public void reload() { + this.keeper.reload(); + BCLib.LOGGER.info("Did Reload " + keeper.getConfigFile()); + } + + @Nullable + public > E getEntry(ConfigKey key, Class type) { + return this.keeper.getEntry(key, type); + } + + @Nullable + public > T getDefault(ConfigKey key, Class type) { + ConfigKeeper.Entry entry = keeper.getEntry(key, type); + return entry != null ? entry.getDefault() : null; + } + + protected String getString(ConfigKey key, String defaultValue) { + String str = keeper.getValue(key, ConfigKeeper.StringEntry.class); + if (str == null) { + ConfigKeeper.StringEntry entry = keeper.registerEntry(key, new ConfigKeeper.StringEntry(defaultValue)); + return entry.getValue(); + } + return str != null ? str : defaultValue; + } + + protected String getString(ConfigKey key) { + String str = keeper.getValue(key, ConfigKeeper.StringEntry.class); + return str != null ? str : ""; + } + + protected boolean setString(ConfigKey key, String value) { + try { + ConfigKeeper.StringEntry entry = keeper.getEntry(key, ConfigKeeper.StringEntry.class); + if (entry == null) return false; + entry.setValue(value); + return true; + } catch (NullPointerException ex) { + BCLib.LOGGER.catching(ex); + } + return false; + } + + protected int getInt(ConfigKey key, int defaultValue) { + Integer val = keeper.getValue(key, ConfigKeeper.IntegerEntry.class); + if (val == null) { + ConfigKeeper.IntegerEntry entry = keeper.registerEntry(key, new ConfigKeeper.IntegerEntry(defaultValue)); + return entry.getValue(); + } + return val != null ? val : defaultValue; + } + + protected int getInt(ConfigKey key) { + Integer val = keeper.getValue(key, ConfigKeeper.IntegerEntry.class); + return val != null ? val : 0; + } + + protected boolean setInt(ConfigKey key, int value) { + try { + ConfigKeeper.IntegerEntry entry = keeper.getEntry(key, ConfigKeeper.IntegerEntry.class); + if (entry == null) return false; + entry.setValue(value); + return true; + } catch (NullPointerException ex) { + BCLib.LOGGER.catching(ex); + } + return false; + } + + protected , RE extends ConfigKeeper.RangeEntry> boolean setRanged(ConfigKey key, + T value, + Class type) { + try { + ConfigKeeper.RangeEntry entry = keeper.getEntry(key, type); + if (entry == null) return false; + entry.setValue(value); + return true; + } catch (NullPointerException | ClassCastException ex) { + BCLib.LOGGER.catching(ex); + } + return false; + } + + protected float getFloat(ConfigKey key, float defaultValue) { + Float val = keeper.getValue(key, ConfigKeeper.FloatEntry.class); + if (val == null) { + ConfigKeeper.FloatEntry entry = keeper.registerEntry(key, new ConfigKeeper.FloatEntry(defaultValue)); + return entry.getValue(); + } + return val; + } + + protected float getFloat(ConfigKey key) { + Float val = keeper.getValue(key, ConfigKeeper.FloatEntry.class); + return val != null ? val : 0.0F; + } + + protected boolean setFloat(ConfigKey key, float value) { + try { + ConfigKeeper.FloatEntry entry = keeper.getEntry(key, ConfigKeeper.FloatEntry.class); + if (entry == null) return false; + entry.setValue(value); + return true; + } catch (NullPointerException ex) { + BCLib.LOGGER.catching(ex); + } + return false; + } + + protected boolean getBoolean(ConfigKey key, boolean defaultValue) { + Boolean val = keeper.getValue(key, ConfigKeeper.BooleanEntry.class); + if (val == null) { + ConfigKeeper.BooleanEntry entry = keeper.registerEntry(key, new ConfigKeeper.BooleanEntry(defaultValue)); + return entry.getValue(); + } + return val; + } + + protected boolean getBoolean(ConfigKey key) { + Boolean val = keeper.getValue(key, ConfigKeeper.BooleanEntry.class); + return val != null ? val : false; + } + + protected boolean setBoolean(ConfigKey key, boolean value) { + try { + ConfigKeeper.BooleanEntry entry = keeper.getEntry(key, ConfigKeeper.BooleanEntry.class); + if (entry == null) return false; + entry.setValue(value); + return true; + } catch (NullPointerException ex) { + BCLib.LOGGER.catching(ex); + } + return false; + } + + protected List getStringArray(ConfigKey key, List defaultValue) { + List str = keeper.getValue(key, ConfigKeeper.StringArrayEntry.class); + if (str == null) { + ConfigKeeper.StringArrayEntry entry = keeper.registerEntry(key, + new ConfigKeeper.StringArrayEntry(defaultValue)); + return entry.getValue(); + } + return str != null ? str : defaultValue; + } + + protected List getStringArray(ConfigKey key) { + List str = keeper.getValue(key, ConfigKeeper.StringArrayEntry.class); + return str != null ? str : new ArrayList<>(0); + } + + protected boolean setStringArray(ConfigKey key, List value) { + try { + ConfigKeeper.StringArrayEntry entry = keeper.getEntry(key, ConfigKeeper.StringArrayEntry.class); + if (entry == null) return false; + entry.setValue(value); + return true; + } catch (NullPointerException ex) { + BCLib.LOGGER.catching(ex); + } + return false; + } +} diff --git a/src/main/java/org/betterx/bclib/config/ConfigKeeper.java b/src/main/java/org/betterx/bclib/config/ConfigKeeper.java new file mode 100644 index 00000000..84179fba --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/ConfigKeeper.java @@ -0,0 +1,457 @@ +package org.betterx.bclib.config; + +import net.minecraft.util.GsonHelper; + +import com.google.common.collect.Maps; +import com.google.common.reflect.TypeToken; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.betterx.bclib.api.dataexchange.handler.autosync.FileContentWrapper; +import org.betterx.bclib.util.JsonFactory; +import org.betterx.bclib.util.Pair; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + +public final class ConfigKeeper { + private final Map> configEntries = Maps.newHashMap(); + private final ConfigWriter writer; + + private JsonObject configObject; + private boolean changed = false; + + public ConfigKeeper(String modID, String group) { + this.writer = new ConfigWriter(modID, group); + this.configObject = writer.load(); + } + + public File getConfigFile() { + return this.writer.getConfigFile(); + } + + boolean compareAndUpdateForSync(FileContentWrapper content) { + ByteArrayInputStream inputStream = content.getInputStream(); + final JsonObject other = JsonFactory.getJsonObject(inputStream); + + boolean changed = this.compareAndUpdateForSync(other); + if (changed) { + OutputStream outStream = content.getEmptyOutputStream(); + JsonFactory.storeJson(outStream, this.configObject); + content.syncWithOutputStream(); + } + return changed; + } + + boolean compareAndUpdateForSync(JsonObject other) { + return compareAndUpdateForSync(this.configObject, other); + } + + private static Pair> find(JsonObject json, Pair key) { + for (var entry : json.entrySet()) { + final Pair otherKey = ConfigKey.realKey(entry.getKey()); + if (otherKey.first.equals(key.first)) return new Pair<>(entry.getValue(), otherKey); + } + + return null; + } + + /** + * Called for content based auto-sync. + * + * @param me - When called in AutoSync this represents the content of the client. + * @param other - When called in AutoSync, this represents the content of the server + * @return {@code true} if content was changed + */ + static boolean compareAndUpdateForSync(JsonObject me, JsonObject other) { + boolean changed = false; + for (var otherEntry : other.entrySet()) { + final Pair otherKey = ConfigKey.realKey(otherEntry.getKey()); + final JsonElement otherValue = otherEntry.getValue(); + + Pair> temp = find(me, otherKey); + //we already have an entry + if (temp != null) { + final Pair myKey = temp.second; + final JsonElement myValue = temp.first; + + if ((otherValue.isJsonNull() && !myValue.isJsonNull()) || (otherValue.isJsonPrimitive() && !myValue.isJsonPrimitive()) || (otherValue.isJsonObject() && !myValue.isJsonObject()) || (otherValue.isJsonArray() && !myValue.isJsonArray())) { + //types are different => replace with "server"-version in other + changed = true; + me.add(myKey.first + myKey.second, otherValue); + } else if (otherValue.isJsonPrimitive() || otherValue.isJsonArray() || otherValue.isJsonNull()) { + if (!otherValue.equals(myValue)) { + changed = true; + me.add(myKey.first + myKey.second, otherValue); + } + } else if (otherValue.isJsonObject()) { + changed |= compareAndUpdateForSync(myValue.getAsJsonObject(), otherValue.getAsJsonObject()); + } + } else { //no entry, just copy the value from other + if (!otherValue.isJsonNull()) { + changed = true; + temp = find(me, otherKey); + me.add(otherKey.first + otherKey.second, otherValue); + } + } + } + + return changed; + } + + + public void save() { + if (!changed) return; + this.writer.save(); + this.changed = false; + } + + void reload() { + this.configObject = this.writer.reload(); + this.configEntries.clear(); + this.changed = false; + } + + private > void initializeEntry(ConfigKey key, E entry) { + if (configObject == null) { + return; + } + String[] path = key.getPath(); + JsonObject obj = configObject; + + if (!key.isRoot()) { + for (String group : path) { + JsonElement element = obj.get(group); + if (element == null || !element.isJsonObject()) { + element = new JsonObject(); + obj.add(group, element); + } + obj = element.getAsJsonObject(); + } + } + + String paramKey = key.getEntry(); + if (entry.hasDefaultInName()) { + paramKey += " [default: " + entry.getDefault() + "]"; + } + + this.changed |= entry.setLocation(obj, paramKey); + } + + private > void storeValue(E entry, T value) { + if (configObject == null) { + return; + } + T val = entry.getValue(); + if (value.equals(val)) return; + entry.toJson(value); + this.changed = true; + } + + private > T getValue(E entry) { + if (!entry.hasLocation()) { + return entry.getDefault(); + } + return entry.fromJson(); + } + + @Nullable + public > E getEntry(ConfigKey key, Class type) { + Entry entry = this.configEntries.get(key); + if (type.isInstance(entry)) { + return type.cast(entry); + } + return null; + } + + @Nullable + public > T getValue(ConfigKey key, Class type) { + Entry entry = this.getEntry(key, type); + if (entry == null) { + return null; + } + return entry.getValue(); + } + + public > E registerEntry(ConfigKey key, E entry) { + entry.setWriter(value -> this.storeValue(entry, value)); + entry.setReader(() -> { + return this.getValue(entry); + }); + this.initializeEntry(key, entry); + this.configEntries.put(key, entry); + return entry; + } + + public static class BooleanEntry extends Entry { + + public BooleanEntry(Boolean defaultValue) { + super(defaultValue); + } + + @Override + public Boolean fromJson() { + return GsonHelper.getAsBoolean(location, key, defaultValue); + } + + @Override + public void toJson(Boolean value) { + this.location.addProperty(key, value); + } + } + + public static class FloatEntry extends Entry { + + public FloatEntry(Float defaultValue) { + super(defaultValue); + } + + @Override + public Float fromJson() { + return GsonHelper.getAsFloat(location, key, defaultValue); + } + + @Override + public void toJson(Float value) { + this.location.addProperty(key, value); + } + } + + public static class FloatRange extends RangeEntry { + + public FloatRange(Float defaultValue, float minVal, float maxVal) { + super(defaultValue, minVal, maxVal); + } + + @Override + public Float fromJson() { + return GsonHelper.getAsFloat(location, key, defaultValue); + } + + @Override + public void toJson(Float value) { + this.location.addProperty(key, value); + } + } + + public static class IntegerEntry extends Entry { + + public IntegerEntry(Integer defaultValue) { + super(defaultValue); + } + + @Override + public Integer getDefault() { + return this.defaultValue; + } + + @Override + public Integer fromJson() { + return GsonHelper.getAsInt(location, key, defaultValue); + } + + @Override + public void toJson(Integer value) { + this.location.addProperty(key, value); + } + } + + public static class IntegerRange extends RangeEntry { + + public IntegerRange(Integer defaultValue, int minVal, int maxVal) { + super(defaultValue, minVal, maxVal); + } + + @Override + public Integer fromJson() { + return GsonHelper.getAsInt(location, key, defaultValue); + } + + @Override + public void toJson(Integer value) { + this.location.addProperty(key, value); + } + } + + public static class StringEntry extends Entry { + + public StringEntry(String defaultValue) { + super(defaultValue); + } + + @Override + public String fromJson() { + return GsonHelper.getAsString(location, key, defaultValue); + } + + @Override + public void toJson(String value) { + this.location.addProperty(key, value); + } + } + + public static abstract class ArrayEntry extends Entry> { + public ArrayEntry(List defaultValue) { + super(defaultValue); + } + + protected abstract T getValue(JsonElement element); + protected abstract void add(JsonArray array, T element); + + private JsonArray toArray(List input) { + final JsonArray array = new JsonArray(); + input.forEach(s -> add(array, s)); + return array; + } + + @Override + public List fromJson() { + final JsonArray resArray = GsonHelper.getAsJsonArray(location, key, toArray(defaultValue)); + final List res = new ArrayList<>(resArray.size()); + resArray.forEach(e -> res.add(getValue(e))); + + return res; + } + + @Override + public void toJson(List value) { + this.location.add(key, toArray(value)); + } + } + + public static class StringArrayEntry extends ArrayEntry { + public StringArrayEntry(List defaultValue) { + super(defaultValue); + } + + @Override + protected String getValue(JsonElement el) { + return el.getAsString(); + } + + protected void add(JsonArray array, String el) { + array.add(el); + } + + @Override + protected boolean hasDefaultInName() { + return false; + } + } + + public static class EnumEntry> extends Entry { + private final Type type; + + public EnumEntry(T defaultValue) { + super(defaultValue); + TypeToken token = new TypeToken() { + private static final long serialVersionUID = 1L; + }; + this.type = token.getType(); + } + + @Override + public T getDefault() { + return this.defaultValue; + } + + @Override + public T fromJson() { + return JsonFactory.GSON.fromJson(location.get(key), type); + } + + @Override + public void toJson(T value) { + location.addProperty(key, JsonFactory.GSON.toJson(value, type)); + } + } + + public static abstract class RangeEntry> extends Entry { + private final T min, max; + + public RangeEntry(T defaultValue, T minVal, T maxVal) { + super(defaultValue); + this.min = minVal; + this.max = maxVal; + } + + @Override + public void setValue(T value) { + super.setValue(value.compareTo(min) < 0 ? min : value.compareTo(max) > 0 ? max : value); + } + + public T minValue() { + return this.min; + } + + public T maxValue() { + return this.max; + } + } + + public static abstract class Entry { + protected final T defaultValue; + protected Consumer writer; + protected Supplier reader; + protected JsonObject location; + protected String key; + + public abstract T fromJson(); + + public abstract void toJson(T value); + + public Entry(T defaultValue) { + this.defaultValue = defaultValue; + } + + protected void setWriter(Consumer writer) { + this.writer = writer; + } + + protected void setReader(Supplier reader) { + this.reader = reader; + } + + protected boolean setLocation(JsonObject location, String key) { + this.location = location; + this.key = key; + if (!location.has(key)) { + this.toJson(defaultValue); + return true; + } + return false; + } + + protected boolean hasLocation() { + return this.location != null && this.key != null; + } + + public T getValue() { + return this.reader.get(); + } + + public void setValue(T value) { + this.writer.accept(value); + } + + public T getDefault() { + return this.defaultValue; + } + + public void setDefault() { + this.setValue(defaultValue); + } + + protected boolean hasDefaultInName() { + return true; + } + } +} diff --git a/src/main/java/org/betterx/bclib/config/ConfigKey.java b/src/main/java/org/betterx/bclib/config/ConfigKey.java new file mode 100644 index 00000000..8ea3e665 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/ConfigKey.java @@ -0,0 +1,97 @@ +package org.betterx.bclib.config; + +import net.minecraft.resources.ResourceLocation; + +import org.betterx.bclib.util.Pair; + +import java.util.Arrays; +import org.jetbrains.annotations.NotNull; + +public class ConfigKey { + private final String[] path; + private final String entry; + private final boolean root; + + public ConfigKey(String entry, String... path) { + this.validate(entry); + this.path = path; + this.entry = entry; + this.root = path.length == 0 || (path.length == 1 && path[0].isEmpty()); + } + + public ConfigKey(String entry, ResourceLocation path) { + this(entry, path.getNamespace(), path.getPath()); + } + + public String[] getPath() { + return path; + } + + public String getEntry() { + return entry; + } + + public boolean isRoot() { + return root; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(path); + result = prime * result + entry.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ConfigKey)) { + return false; + } + ConfigKey other = (ConfigKey) obj; + if (other.path.length != path.length) { + return false; + } + for (int i = 0; i < path.length; i++) { + if (!path[i].equals(other.path[i])) { + return false; + } + } + return entry.equals(other.entry); + } + + @Override + public String toString() { + if (root) { + return String.format("[root]:%s", entry); + } + String p = path[0]; + for (int i = 1; i < path.length; i++) { + p += "." + path[i]; + } + return String.format("%s:%s", p, entry); + } + + private void validate(String entry) { + if (entry == null) { + throw new NullPointerException("Config key must be not null!"); + } + if (entry.isEmpty()) { + throw new IndexOutOfBoundsException("Config key must be not empty!"); + } + } + + public static Pair realKey(@NotNull String key) { + String[] parts = key.split("\\[default:", 2); + if (parts.length == 1) { + return new Pair(parts[0].trim(), ""); + } else if (parts.length == 2) { + return new Pair(parts[0].trim(), " " + ("[default:" + parts[1]).trim()); + } + return new Pair(key, ""); + } +} diff --git a/src/main/java/org/betterx/bclib/config/ConfigUI.java b/src/main/java/org/betterx/bclib/config/ConfigUI.java new file mode 100644 index 00000000..2e7942c7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/ConfigUI.java @@ -0,0 +1,25 @@ +package org.betterx.bclib.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface ConfigUI { + /** + * When {@code true}, this option will not generate UI-Elements. + */ + boolean hide() default false; + + /** + * When a Widget is generated for this option, it will be indented by this Value + */ + int leftPadding() default 0; + + /** + * When a Widget is generated for this option, it will be indented by this Value + */ + int topPadding() default 0; +} diff --git a/src/main/java/org/betterx/bclib/config/ConfigWriter.java b/src/main/java/org/betterx/bclib/config/ConfigWriter.java new file mode 100644 index 00000000..a69efc82 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/ConfigWriter.java @@ -0,0 +1,72 @@ +package org.betterx.bclib.config; + +import net.fabricmc.loader.api.FabricLoader; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.betterx.bclib.util.JsonFactory; + +import java.io.File; +import java.nio.file.Path; + +public class ConfigWriter { + private final static Path GAME_CONFIG_DIR = FabricLoader.getInstance().getConfigDir(); + + private final File configFile; + private JsonObject configObject; + + public ConfigWriter(String modID, String configFile) { + this.configFile = new File(GAME_CONFIG_DIR.resolve(modID).toFile(), configFile + ".json"); + File parent = this.configFile.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } + this.load(); + } + + File getConfigFile() { + return this.configFile; + } + + public JsonObject getConfig() { + return configObject; + } + + public void save() { + if (configObject == null) { + return; + } + save(configFile, configObject); + } + + JsonObject reload() { + configObject = load(configFile); + return configObject; + } + + public JsonObject load() { + if (configObject == null) { + configObject = load(configFile); + } + return configObject; + } + + public void save(JsonElement config) { + this.configObject = config.getAsJsonObject(); + save(configFile, config); + } + + public static JsonObject load(File configFile) { + return JsonFactory.getJsonObject(configFile); + } + + public static void save(File configFile, JsonElement config) { + JsonFactory.storeJson(configFile, config); + } + + public static String scrubFileName(String input) { + input = input.replaceAll("[/\\ ]+", "_"); + input = input.replaceAll("[,:&\"\\|\\<\\>\\?\\*]", "_"); + return input; + } +} diff --git a/src/main/java/org/betterx/bclib/config/Configs.java b/src/main/java/org/betterx/bclib/config/Configs.java new file mode 100644 index 00000000..6c81d8c4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/Configs.java @@ -0,0 +1,42 @@ +package org.betterx.bclib.config; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.BCLib; + +import java.util.Collections; + +public class Configs { + // Client and Server-Config must be the first entries. They are not part of the Auto-Sync process + // But will be needed by other Auto-Sync Config-Files + @Environment(EnvType.CLIENT) + public static final ClientConfig CLIENT_CONFIG = new ClientConfig(); + public static final ServerConfig SERVER_CONFIG = new ServerConfig(); + + public static final GeneratorConfig GENERATOR_CONFIG = new GeneratorConfig(); + public static final MainConfig MAIN_CONFIG = new MainConfig(); + + public static final PathConfig RECIPE_CONFIG = new PathConfig(BCLib.MOD_ID, "recipes"); + public static final PathConfig BIOMES_CONFIG = new PathConfig(BCLib.MOD_ID, "biomes", false); + + public static final String MAIN_PATCH_CATEGORY = "patches"; + + public static void save() { + MAIN_CONFIG.saveChanges(); + RECIPE_CONFIG.saveChanges(); + GENERATOR_CONFIG.saveChanges(); + BIOMES_CONFIG.saveChanges(); + } + + static { + BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("end_land_biomes", "force_include"), + new ConfigKeeper.StringArrayEntry(Collections.EMPTY_LIST)); + BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("end_void_biomes", "force_include"), + new ConfigKeeper.StringArrayEntry(Collections.EMPTY_LIST)); + BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("nether_biomes", "force_include"), + new ConfigKeeper.StringArrayEntry(Collections.EMPTY_LIST)); + BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("nether_biomes", "force_exclude"), + new ConfigKeeper.StringArrayEntry(Collections.EMPTY_LIST)); + } +} diff --git a/src/main/java/org/betterx/bclib/config/EntryConfig.java b/src/main/java/org/betterx/bclib/config/EntryConfig.java new file mode 100644 index 00000000..44276cfc --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/EntryConfig.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.config; + +public class EntryConfig extends IdConfig { + public EntryConfig(String modID, String group) { + super(modID, group, (id, entry) -> { + return new ConfigKey(entry, id); + }); + } +} diff --git a/src/main/java/org/betterx/bclib/config/GeneratorConfig.java b/src/main/java/org/betterx/bclib/config/GeneratorConfig.java new file mode 100644 index 00000000..8e4e9680 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/GeneratorConfig.java @@ -0,0 +1,18 @@ +package org.betterx.bclib.config; + +import org.betterx.bclib.BCLib; + +public class GeneratorConfig extends NamedPathConfig { + public static final ConfigToken USE_OLD_GENERATOR = ConfigToken.Boolean(false, + "useOldBiomeGenerator", + "options"); + + + public GeneratorConfig() { + super(BCLib.MOD_ID, "generator", false); + } + + public boolean useOldGenerator() { + return get(USE_OLD_GENERATOR); + } +} diff --git a/src/main/java/org/betterx/bclib/config/IdConfig.java b/src/main/java/org/betterx/bclib/config/IdConfig.java new file mode 100644 index 00000000..c51afd5a --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/IdConfig.java @@ -0,0 +1,89 @@ +package org.betterx.bclib.config; + +import net.minecraft.resources.ResourceLocation; + +import java.util.function.BiFunction; +import org.jetbrains.annotations.Nullable; + +public class IdConfig extends Config { + protected final BiFunction keyFactory; + + public IdConfig(String modID, String group, BiFunction keyFactory) { + super(modID, group); + this.keyFactory = keyFactory; + } + + @Override + protected void registerEntries() { + } + + protected ConfigKey createKey(ResourceLocation id, String key) { + return this.keyFactory.apply(id, key); + } + + @Nullable + public > E getEntry(ResourceLocation id, String key, Class type) { + return this.getEntry(createKey(id, key), type); + } + + @Nullable + public > T getDefault(ResourceLocation id, String key, Class type) { + return this.getDefault(createKey(id, key), type); + } + + public String getString(ResourceLocation id, String key, String defaultValue) { + return this.getString(createKey(id, key), defaultValue); + } + + public String getString(ResourceLocation id, String key) { + return this.getString(createKey(id, key)); + } + + public boolean setString(ResourceLocation id, String key, String value) { + return this.setString(createKey(id, key), value); + } + + public int getInt(ResourceLocation id, String key, int defaultValue) { + return this.getInt(createKey(id, key), defaultValue); + } + + public int getInt(ResourceLocation id, String key) { + return this.getInt(createKey(id, key)); + } + + public boolean setInt(ResourceLocation id, String key, int value) { + return this.setInt(createKey(id, key), value); + } + + public boolean setRangedInt(ResourceLocation id, String key, int value) { + return this.setRanged(createKey(id, key), value, ConfigKeeper.IntegerRange.class); + } + + public boolean setRangedFloat(ResourceLocation id, String key, float value) { + return this.setRanged(createKey(id, key), value, ConfigKeeper.FloatRange.class); + } + + public float getFloat(ResourceLocation id, String key, float defaultValue) { + return this.getFloat(createKey(id, key), defaultValue); + } + + public float getFloat(ResourceLocation id, String key) { + return this.getFloat(createKey(id, key)); + } + + public boolean setFloat(ResourceLocation id, String key, float value) { + return this.setFloat(createKey(id, key), value); + } + + public boolean getBoolean(ResourceLocation id, String key, boolean defaultValue) { + return this.getBoolean(createKey(id, key), defaultValue); + } + + public boolean getBoolean(ResourceLocation id, String key) { + return this.getBoolean(createKey(id, key)); + } + + public boolean setBoolean(ResourceLocation id, String key, boolean value) { + return this.setBoolean(createKey(id, key), value); + } +} diff --git a/src/main/java/org/betterx/bclib/config/MainConfig.java b/src/main/java/org/betterx/bclib/config/MainConfig.java new file mode 100644 index 00000000..93ccaa53 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/MainConfig.java @@ -0,0 +1,28 @@ +package org.betterx.bclib.config; + +import org.betterx.bclib.BCLib; + +public class MainConfig extends NamedPathConfig { + public static final ConfigToken APPLY_PATCHES = ConfigToken.Boolean(true, + "applyPatches", + Configs.MAIN_PATCH_CATEGORY); + + @ConfigUI(leftPadding = 8) + public static final ConfigToken REPAIR_BIOMES = DependendConfigToken.Boolean(true, + "repairBiomesOnLoad", + Configs.MAIN_PATCH_CATEGORY, + (config) -> config.get( + APPLY_PATCHES)); + + public MainConfig() { + super(BCLib.MOD_ID, "main", true, true); + } + + public boolean applyPatches() { + return get(APPLY_PATCHES); + } + + public boolean repairBiomes() { + return get(REPAIR_BIOMES); + } +} diff --git a/src/main/java/org/betterx/bclib/config/NamedPathConfig.java b/src/main/java/org/betterx/bclib/config/NamedPathConfig.java new file mode 100644 index 00000000..48f32354 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/NamedPathConfig.java @@ -0,0 +1,270 @@ +package org.betterx.bclib.config; + +import net.minecraft.resources.ResourceLocation; + +import org.betterx.bclib.BCLib; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Predicate; + +public class NamedPathConfig extends PathConfig { + public static class ConfigTokenDescription { + public final ConfigToken token; + public final String internalName; + public final Boolean hidden; + public final int leftPadding; + public final int topPadding; + + @SuppressWarnings("unchecked") + ConfigTokenDescription(Field fl) throws IllegalAccessException { + token = (ConfigToken) fl.get(null); + internalName = fl.getName(); + + ConfigUI ui = fl.getAnnotation(ConfigUI.class); + if (ui != null) { + this.hidden = ui.hide(); + leftPadding = ui.leftPadding(); + topPadding = ui.topPadding(); + } else { + this.hidden = false; + this.leftPadding = 0; + topPadding = 0; + } + + } + + public String getPath() { + StringBuilder path = new StringBuilder(); + for (String p : token.getPath()) { + path.append(".") + .append(p); + + } + path.append(".").append(token.getEntry()); + return path.toString(); + } + } + + public static class DependendConfigToken extends ConfigToken { + protected final Predicate dependenciesTrue; + + protected DependendConfigToken(Class type, + T defaultValue, + String entry, + ResourceLocation path, + Predicate dependenciesTrue) { + this(type, defaultValue, entry, new String[]{path.getNamespace(), path.getPath()}, dependenciesTrue); + } + + protected DependendConfigToken(Class type, + T defaultValue, + String entry, + String path, + Predicate dependenciesTrue) { + super(type, defaultValue, entry, path); + this.dependenciesTrue = dependenciesTrue; + } + + protected DependendConfigToken(Class type, + T defaultValue, + String entry, + String[] path, + Predicate dependenciesTrue) { + super(type, defaultValue, entry, path); + this.dependenciesTrue = dependenciesTrue; + } + + public boolean dependenciesTrue(NamedPathConfig config) { + return dependenciesTrue.test(config); + } + + public static DependendConfigToken Boolean(boolean defaultValue, + String entry, + String path, + Predicate dependenciesTrue) { + return new DependendConfigToken(ConfigKeeper.BooleanEntry.class, + defaultValue, + entry, + path, + dependenciesTrue); + } + } + + public static class ConfigToken extends ConfigKey { + public final T defaultValue; + public final Class type; + + protected ConfigToken(Class type, T defaultValue, String entry, ResourceLocation path) { + this(type, defaultValue, entry, path.getNamespace(), path.getPath()); + } + + @SuppressWarnings("unchecked") + protected ConfigToken(Class type, T defaultValue, String entry, String... path) { + super(entry, path); + this.defaultValue = defaultValue; + + this.type = type; + } + + public boolean dependenciesTrue(NamedPathConfig config) { + return true; + } + + public static ConfigToken Boolean(boolean defaultValue, String entry, String path) { + return new ConfigToken(ConfigKeeper.BooleanEntry.class, defaultValue, entry, path); + } + + public static ConfigToken Int(int defaultValue, String entry, String path) { + return new ConfigToken(ConfigKeeper.IntegerEntry.class, defaultValue, entry, path); + } + + public static ConfigToken Float(float defaultValue, String entry, String path) { + return new ConfigToken(ConfigKeeper.FloatEntry.class, defaultValue, entry, path); + } + + public static ConfigToken String(String defaultValue, String entry, String path) { + return new ConfigToken(ConfigKeeper.StringEntry.class, defaultValue, entry, path); + } + + public static ConfigToken> StringArray(List defaultValue, String entry, String path) { + return new ConfigToken>(ConfigKeeper.StringArrayEntry.class, defaultValue, entry, path); + } + } + + public NamedPathConfig(String modID, String group, boolean autoSync, boolean diffContent) { + super(modID, group, autoSync, diffContent); + onInit(); + } + + public NamedPathConfig(String modID, String group, boolean autoSync) { + super(modID, group, autoSync); + onInit(); + } + + public NamedPathConfig(String modID, String group) { + super(modID, group); + onInit(); + } + + public List> getAllOptions() { + List> res = new LinkedList<>(); + for (Field fl : this.getClass().getDeclaredFields()) { + int modifiers = fl.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && ConfigToken.class.isAssignableFrom(fl.getType())) { + try { + res.add(new ConfigTokenDescription<>(fl)); + } catch (IllegalAccessException e) { + BCLib.LOGGER.error("Could not access " + fl); + } + } + } + return res; + } + + protected void onInit() { + getAllOptions().forEach(e -> get(e.token)); + this.saveChanges(); + } + + /** + * The value without any check of {@link DependendConfigToken} + *

+ * In most cases you probably want to use {@link #get(ConfigToken)}, we use this Method if we + * present the actual value of the Settings from the Config File without any additional processing. + * + * @param what The Option you want to get + * @param The Type of the Option + * @return The Value of the Option (without checking the {@link DependendConfigToken}): + */ + public T getRaw(ConfigToken what) { + return _get(what, true); + } + + /** + * The value of an Option + * + * @param what he Option you want to get + * @param The Type of the Option + * @return The Value of the Option. If this option is a {@link DependendConfigToken}, the returned value + * may not be the value from the config File. For Example, on a {@link Boolean}-Type the result is always false + * if {@link DependendConfigToken#dependenciesTrue} returns {@code false}. + */ + public T get(ConfigToken what) { + return _get(what, false); + } + + @SuppressWarnings("unchecked") + private T _get(ConfigToken what, boolean raw) { + //TODO: Check if we can make config fully Generic to avoid runtime type checks... + if (ConfigKeeper.BooleanEntry.class.isAssignableFrom(what.type)) { + return (T) _getBoolean((ConfigToken) what, raw); + } + if (ConfigKeeper.IntegerEntry.class.isAssignableFrom(what.type)) { + return (T) _getInt((ConfigToken) what); + } + if (ConfigKeeper.FloatEntry.class.isAssignableFrom(what.type)) { + return (T) _getFloat((ConfigToken) what); + } + if (ConfigKeeper.StringEntry.class.isAssignableFrom(what.type)) { + return (T) _getString((ConfigToken) what); + } + if (ConfigKeeper.StringArrayEntry.class.isAssignableFrom(what.type)) { + return (T) _getStringArray((ConfigToken>) what); + } + return this._get(what); + } + + private T _get(ConfigToken what) { + BCLib.LOGGER.error(what + " has unsupported Type."); + return what.defaultValue; + } + + public void set(ConfigToken what, boolean value) { + this.setBoolean(what, value); + } + + private Boolean _getBoolean(ConfigToken what, boolean raw) { + if (!raw && !what.dependenciesTrue(this)) { + return false; + } + + return this.getBoolean(what, what.defaultValue); + } + + public void set(ConfigToken what, int value) { + this.setInt(what, value); + } + + private Integer _getInt(ConfigToken what) { + return this.getInt(what, what.defaultValue); + } + + public void set(ConfigToken what, float value) { + this.setFloat(what, value); + } + + private Float _getFloat(ConfigToken what) { + return this.getFloat(what, what.defaultValue); + } + + public void set(ConfigToken what, String value) { + this.setString(what, value); + } + + private String _getString(ConfigToken what) { + return this.getString(what, what.defaultValue); + } + + public void set(ConfigToken> what, List value) { + this.setStringArray(what, value); + } + + private List _getStringArray(ConfigToken> what) { + return this.getStringArray(what, what.defaultValue); + } + + +} diff --git a/src/main/java/org/betterx/bclib/config/PathConfig.java b/src/main/java/org/betterx/bclib/config/PathConfig.java new file mode 100644 index 00000000..2d1240f8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/PathConfig.java @@ -0,0 +1,166 @@ +package org.betterx.bclib.config; + +import java.util.List; +import org.jetbrains.annotations.Nullable; + +public class PathConfig extends Config { + public PathConfig(String modID, String group, boolean autoSync, boolean diffContent) { + super(modID, group, autoSync, diffContent); + } + + public PathConfig(String modID, String group, boolean autoSync) { + super(modID, group, autoSync); + } + + public PathConfig(String modID, String group) { + super(modID, group); + } + + @Override + protected void registerEntries() { + } + + protected static ConfigKey createKey(String category, String key) { + return new ConfigKey(key, category.split("\\.")); + } + + protected static ConfigKey createKey(String key) { + return createKey("", key); + } + + @Nullable + public > E getEntry(String category, String key, Class type) { + return this.getEntry(createKey(category, key), type); + } + + @Nullable + public > T getDefault(String category, String key, Class type) { + return this.getDefault(createKey(category, key), type); + } + + public String getString(String category, String key, String defaultValue) { + return this.getString(createKey(category, key), defaultValue); + } + + public String getString(String category, String key) { + return this.getString(createKey(category, key)); + } + + public boolean setString(String category, String key, String value) { + return this.setString(createKey(category, key), value); + } + + public int getInt(String category, String key, int defaultValue) { + return this.getInt(createKey(category, key), defaultValue); + } + + public int getInt(String category, String key) { + return this.getInt(createKey(category, key)); + } + + public boolean setInt(String category, String key, int value) { + return this.setInt(createKey(category, key), value); + } + + public boolean setRangedInt(String category, String key, int value) { + return this.setRanged(createKey(category, key), value, ConfigKeeper.IntegerRange.class); + } + + public boolean setRangedFloat(String category, String key, float value) { + return this.setRanged(createKey(category, key), value, ConfigKeeper.FloatRange.class); + } + + public float getFloat(String category, String key, float defaultValue) { + return this.getFloat(createKey(category, key), defaultValue); + } + + public float getFloat(String category, String key) { + return this.getFloat(createKey(category, key)); + } + + public boolean setFloat(String category, String key, float value) { + return this.setFloat(createKey(category, key), value); + } + + public boolean getBoolean(String category, String key, boolean defaultValue) { + return this.getBoolean(createKey(category, key), defaultValue); + } + + public boolean getBoolean(String category, String key) { + return this.getBoolean(createKey(category, key)); + } + + public boolean setBoolean(String category, String key, boolean value) { + return this.setBoolean(createKey(category, key), value); + } + + public List getStringArray(String category, String key, List defaultValue) { + return this.getStringArray(createKey(category, key), defaultValue); + } + + public List getStringArray(String category, String key) { + return this.getStringArray(createKey(category, key)); + } + + public boolean setStringArray(String category, String key, List value) { + return this.setStringArray(createKey(category, key), value); + } + + // From Root + + public String getStringRoot(String key, String defaultValue) { + return this.getString(createKey(key), defaultValue); + } + + public String getStringRoot(String key) { + return this.getString(createKey(key)); + } + + public boolean setStringRoot(String key, String value) { + return this.setString(createKey(key), value); + } + + public int getIntRoot(String key, int defaultValue) { + return this.getInt(createKey(key), defaultValue); + } + + public int getIntRoot(String key) { + return this.getInt(createKey(key)); + } + + public boolean setIntRoot(String key, int value) { + return this.setInt(createKey(key), value); + } + + public boolean setRangedIntRoot(String key, int value) { + return this.setRanged(createKey(key), value, ConfigKeeper.IntegerRange.class); + } + + public boolean setRangedFloatRoot(String key, float value) { + return this.setRanged(createKey(key), value, ConfigKeeper.FloatRange.class); + } + + public float getFloatRoot(String key, float defaultValue) { + return this.getFloat(createKey(key), defaultValue); + } + + public float getFloatRoot(String key) { + return this.getFloat(createKey(key)); + } + + public boolean setFloatRoot(String key, float value) { + return this.setFloat(createKey(key), value); + } + + public boolean getBooleanRoot(String key, boolean defaultValue) { + return this.getBoolean(createKey(key), defaultValue); + } + + public boolean getBooleanRoot(String key) { + return this.getBoolean(createKey(key)); + } + + public boolean setBooleanRoot(String key, boolean value) { + return this.setBoolean(createKey(key), value); + } +} diff --git a/src/main/java/org/betterx/bclib/config/ServerConfig.java b/src/main/java/org/betterx/bclib/config/ServerConfig.java new file mode 100644 index 00000000..9c644d8e --- /dev/null +++ b/src/main/java/org/betterx/bclib/config/ServerConfig.java @@ -0,0 +1,74 @@ +package org.betterx.bclib.config; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.dataexchange.handler.autosync.AutoSync; + +import java.util.ArrayList; +import java.util.List; + +public class ServerConfig extends NamedPathConfig { + public static final ConfigToken ENABLED = ConfigToken.Boolean(true, "enabled", AutoSync.SYNC_CATEGORY); + public static final DependendConfigToken OFFER_CONFIGS = DependendConfigToken.Boolean(true, + "offerConfigs", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + public static final DependendConfigToken OFFER_FILES = DependendConfigToken.Boolean(true, + "offerFiles", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + public static final DependendConfigToken OFFER_MODS = DependendConfigToken.Boolean(true, + "offerMods", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + public static final DependendConfigToken OFFER_ALL_MODS = DependendConfigToken.Boolean(false, + "offerAllMods", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + OFFER_MODS)); + public static final DependendConfigToken SEND_ALL_MOD_INFO = DependendConfigToken.Boolean(false, + "sendAllModInfo", + AutoSync.SYNC_CATEGORY, + (config) -> config.get( + ENABLED)); + + + public static final ConfigToken> ADDITIONAL_MODS = ConfigToken.StringArray(new ArrayList<>(0), + "additionalMods", + AutoSync.SYNC_CATEGORY); + public static final ConfigToken> EXCLUDED_MODS = ConfigToken.StringArray(new ArrayList<>(0), + "excludeMods", + AutoSync.SYNC_CATEGORY); + + + public ServerConfig() { + super(BCLib.MOD_ID, "server", false); + } + + public boolean isAllowingAutoSync() { + return get(ENABLED); + } + + public boolean isOfferingConfigs() { + return get(OFFER_CONFIGS) /*&& isAllowingAutoSync()*/; + } + + public boolean isOfferingFiles() { + return get(OFFER_FILES) /*&& isAllowingAutoSync()*/; + } + + public boolean isOfferingMods() { + return get(OFFER_MODS) /*&& isAllowingAutoSync()*/; + } + + public boolean isOfferingAllMods() { + return get(OFFER_ALL_MODS) /*&& isAllowingAutoSync()*/; + } + + public boolean isOfferingInfosForMods() { + return get(SEND_ALL_MOD_INFO) /*&& isAllowingAutoSync()*/; + } + +} diff --git a/src/main/java/ru/bclib/entity/BCLEntityWrapper.java b/src/main/java/org/betterx/bclib/entity/BCLEntityWrapper.java similarity index 72% rename from src/main/java/ru/bclib/entity/BCLEntityWrapper.java rename to src/main/java/org/betterx/bclib/entity/BCLEntityWrapper.java index dc4b4f9e..f8025dbb 100644 --- a/src/main/java/ru/bclib/entity/BCLEntityWrapper.java +++ b/src/main/java/org/betterx/bclib/entity/BCLEntityWrapper.java @@ -1,7 +1,7 @@ -package ru.bclib.entity; +package org.betterx.bclib.entity; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; -public record BCLEntityWrapper(EntityType type, boolean canSpawn) { +public record BCLEntityWrapper(EntityType type, boolean canSpawn) { } diff --git a/src/main/java/org/betterx/bclib/entity/DespawnableAnimal.java b/src/main/java/org/betterx/bclib/entity/DespawnableAnimal.java new file mode 100644 index 00000000..4a39b451 --- /dev/null +++ b/src/main/java/org/betterx/bclib/entity/DespawnableAnimal.java @@ -0,0 +1,16 @@ +package org.betterx.bclib.entity; + +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.animal.Animal; +import net.minecraft.world.level.Level; + +public abstract class DespawnableAnimal extends Animal { + protected DespawnableAnimal(EntityType entityType, Level level) { + super(entityType, level); + } + + @Override + public boolean removeWhenFarAway(double d) { + return !this.hasCustomName(); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridCell.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridCell.java new file mode 100644 index 00000000..9b10067b --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridCell.java @@ -0,0 +1,32 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.mojang.blaze3d.vertex.PoseStack; +import org.betterx.bclib.interfaces.TriConsumer; + +import java.util.List; +import java.util.function.Function; + +@Environment(EnvType.CLIENT) +class GridCell extends GridCellDefinition { + public final float height; + Function componentPlacer; + TriConsumer customRender; + + GridCell(double width, + double height, + GridLayout.GridValueType widthType, + Function componentPlacer, + TriConsumer customRender) { + super(width, widthType); + this.height = (float) height; + this.componentPlacer = componentPlacer; + this.customRender = customRender; + } + + protected GridElement buildElementAt(int left, int top, int width, final List collector) { + return new GridElement(left, top, width, (int) this.height, componentPlacer, customRender); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridCheckboxCell.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridCheckboxCell.java new file mode 100644 index 00000000..cc430654 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridCheckboxCell.java @@ -0,0 +1,95 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.components.Checkbox; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import java.util.function.Consumer; + +@Environment(EnvType.CLIENT) +class SignalingCheckBox extends Checkbox { + private final Consumer onChange; + + public SignalingCheckBox(int left, + int top, + int width, + int height, + Component component, + boolean checked, + Consumer onChange) { + super(left, top, width, height, component, checked); + this.onChange = onChange; + if (onChange != null) + onChange.accept(checked); + } + + @Override + public void onPress() { + super.onPress(); + if (onChange != null) + onChange.accept(this.selected()); + } +} + +@Environment(EnvType.CLIENT) +public class GridCheckboxCell extends GridCell implements GridWidgetWithEnabledState { + private boolean checked; + private Checkbox lastCheckbox; + private boolean enabled; + private final float alpha; + + GridCheckboxCell(Component text, + boolean checked, + float alpha, + double width, + GridLayout.GridValueType widthType, + double height) { + this(text, checked, alpha, width, widthType, height, null); + } + + GridCheckboxCell(Component text, + boolean checked, + float alpha, + double width, + GridLayout.GridValueType widthType, + double height, + Consumer onChange) { + super(width, height, widthType, null, null); + lastCheckbox = null; + enabled = true; + this.alpha = alpha; + this.componentPlacer = (transform) -> { + Checkbox cb = new SignalingCheckBox(transform.left, transform.top, transform.width, transform.height, + text, + checked, + (state) -> { + this.checked = state; + if (onChange != null) onChange.accept(state); + } + ); + cb.setAlpha(alpha); + lastCheckbox = cb; + setEnabled(enabled); + return cb; + }; + + } + + public boolean isChecked() { + return checked; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + if (lastCheckbox != null) { + lastCheckbox.active = enabled; + lastCheckbox.setAlpha(enabled ? alpha : (alpha * 0.5f)); + } + this.enabled = enabled; + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridColumn.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridColumn.java new file mode 100644 index 00000000..185685e8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridColumn.java @@ -0,0 +1,76 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.gridlayout.GridLayout.GridValueType; +import org.betterx.bclib.gui.gridlayout.GridLayout.VerticalAlignment; + +import java.util.List; + +@Environment(EnvType.CLIENT) +public class GridColumn extends GridContainer { + GridColumn(double width) { + super(width); + } + + GridColumn(double width, GridLayout.GridValueType widthType) { + super(width, widthType); + } + + public GridRow addRow() { + return addRow(VerticalAlignment.TOP); + } + + public GridRow addRow(VerticalAlignment align) { + GridRow row = new GridRow(1.0, + widthType == GridValueType.INHERIT + ? GridValueType.INHERIT + : GridLayout.GridValueType.PERCENTAGE, + align); + this.cells.add(row); + return row; + } + + + public void addSpacerRow() { + this.addSpacerRow(4); + } + + public void addSpacerRow(int height) { + GridCell cell = new GridCell(1.0, height, GridValueType.PERCENTAGE, null, null); + this.cells.add(cell); + } + + @Override + public int calculateWidth(final int parentWidth) { + if (widthType == GridValueType.INHERIT) { + return cells.stream() + .filter(row -> row.widthType == GridValueType.INHERIT) + .map(row -> row.buildElement(0, 0, 1, 0, 0, null).width) + .reduce(0, (p, c) -> Math.max(p, c)); + + } else { + return super.calculateWidth(parentWidth); + } + } + + + @Override + protected GridElement buildElementAt(int left, int inTop, int width, final List collector) { + int height = 0; + int top = inTop; + + if (widthType == GridValueType.INHERIT) { + width = calculateWidth(width); + } + + for (GridCellDefinition row : cells) { + GridElement element = row.buildElement(width, 0, 1, left, top, collector); + top += element.height; + height += element.height; + } + + return new GridElement(left, inTop, width, height); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridCustomRenderCell.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridCustomRenderCell.java new file mode 100644 index 00000000..8091d42c --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridCustomRenderCell.java @@ -0,0 +1,18 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.mojang.blaze3d.vertex.PoseStack; +import org.betterx.bclib.gui.gridlayout.GridLayout.GridValueType; + + +@Environment(EnvType.CLIENT) +public abstract class GridCustomRenderCell extends GridCell { + protected GridCustomRenderCell(double width, GridValueType widthType, double height) { + super(width, height, widthType, null, null); + this.customRender = this::onRender; + } + + public abstract void onRender(PoseStack poseStack, GridTransform transform, Object context); +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridImageCell.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridImageCell.java new file mode 100644 index 00000000..9db7fd0e --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridImageCell.java @@ -0,0 +1,47 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import org.betterx.bclib.gui.gridlayout.GridLayout.GridValueType; + +@Environment(EnvType.CLIENT) +public class GridImageCell extends GridCell { + GridImageCell(ResourceLocation location, + double width, + GridValueType widthType, + double height, + float alpha, + int uvLeft, + int uvTop, + int uvWidth, + int uvHeight, + int resourceWidth, + int resourceHeight) { + super(width, height, widthType, null, (poseStack, transform, context) -> { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, location); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, + GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, alpha); + GuiComponent.blit(poseStack, + transform.left, + transform.top, + transform.width, + transform.height, + uvLeft, + uvTop, + uvWidth, + uvHeight, + resourceWidth, + resourceHeight); + }); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridLayout.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridLayout.java new file mode 100644 index 00000000..b645eede --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridLayout.java @@ -0,0 +1,220 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.components.AbstractWidget; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.mojang.blaze3d.vertex.PoseStack; +import org.betterx.bclib.gui.gridlayout.GridLayout.GridValueType; +import org.betterx.bclib.interfaces.TriConsumer; +import org.betterx.bclib.util.Pair; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; + + +@Environment(EnvType.CLIENT) +abstract class GridCellDefinition { + public final float width; + public final GridLayout.GridValueType widthType; + + public GridCellDefinition(double width, GridLayout.GridValueType widthType) { + this.width = (float) width; + this.widthType = widthType; + } + + public int calculateWidth(final int parentWidth) { + if (widthType == GridLayout.GridValueType.CONSTANT) { + return (int) this.width; + } else if (widthType == GridValueType.PERCENTAGE) { + return (int) (this.width * parentWidth); + } else { + return 0; + } + } + + final GridElement buildElement(final int parentWidth, + final int autoWidth, + final float autoWidthSum, + int left, + final int top, + final List collector) { + final int width = widthType == GridValueType.FILL + ? (int) ((this.width / autoWidthSum) * autoWidth) + : calculateWidth(parentWidth); + + final GridElement el = buildElementAt(left, top, width, collector); + if (collector != null) { + collector.add(el); + } + return el; + } + + abstract protected GridElement buildElementAt(int left, int top, int width, final List collector); +} + +@Environment(EnvType.CLIENT) +class GridElement extends GridTransform { + final Function componentPlacer; + final TriConsumer customRender; + Object renderContext; + + GridElement(int left, + int top, + int width, + int height, + Function componentPlacer, + TriConsumer customRender) { + super(left, top, width, height); + this.componentPlacer = componentPlacer; + this.customRender = customRender; + } + + GridElement(int left, int top, int width, int height) { + this(left, top, width, height, null, null); + } + + GridTransform transformWithPadding(int leftPadding, int topPadding) { + return new GridTransform(left + leftPadding, top + topPadding, width, height); + } +} + +@Environment(EnvType.CLIENT) +abstract class GridContainer extends GridCellDefinition { + protected List cells; + + public GridContainer(double width) { + this(width, GridLayout.GridValueType.CONSTANT); + } + + GridContainer(double width, GridLayout.GridValueType widthType) { + super(width, widthType); + cells = new LinkedList<>(); + } +} + +@Environment(EnvType.CLIENT) +public class GridLayout extends GridColumn { + public static final int COLOR_WHITE = 0xFFFFFFFF; + 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; + + public final GridScreen screen; + public final int screenHeight; + public final int sidePadding; + public final int initialTopPadding; + public final boolean centerVertically; + private int height; + private int topPadding; + + private List elements; + + public GridLayout(GridScreen screen) { + this(screen, 0, true); + } + + public GridLayout(GridScreen screen, int topPadding, boolean centerVertically) { + this(screen, topPadding, 20, centerVertically); + } + + public GridLayout(GridScreen screen, int topPadding, int sidePadding, boolean centerVertically) { + super(screen.width - 2 * sidePadding, GridValueType.CONSTANT); + this.screen = screen; + this.screenHeight = screen.height; + height = 0; + this.topPadding = topPadding; + this.sidePadding = sidePadding; + this.initialTopPadding = topPadding; + this.centerVertically = centerVertically; + } + + public int getHeight() { + return height; + } + + public int getTopPadding() { + return topPadding; + } + + void buildLayout() { + elements = new LinkedList<>(); + GridElement el = this.buildElement((int) this.width, 0, 1, 0, 0, elements); + this.height = el.height; + if (centerVertically && el.height + initialTopPadding < screenHeight) { + topPadding = (screenHeight - el.height) >> 1; + } else { + topPadding = initialTopPadding; + } + + } + + public List> movableWidgets = new LinkedList<>(); + + public void finalizeLayout() { + buildLayout(); + + elements + .stream() + .filter(element -> element.componentPlacer != null) + .forEach(element -> { + final GridTransform transform = element.transformWithPadding(sidePadding, topPadding); + final Object context = element.componentPlacer.apply(transform); + if (element.customRender != null) { + element.renderContext = context; + } else if (context instanceof AbstractWidget) { + final AbstractWidget widget = (AbstractWidget) context; + movableWidgets.add(new Pair(widget, widget.y)); + screen.addRenderableWidget(widget); + } + }); + } + + public void render(PoseStack poseStack) { + if (elements == null) return; + elements + .stream() + .filter(element -> element.customRender != null) + .forEach(element -> element.customRender.accept(poseStack, + element.transformWithPadding(sidePadding, topPadding), + element.renderContext)); + } + + + public enum VerticalAlignment { + TOP, CENTER, BOTTOM + } + + public enum Alignment { + LEFT, CENTER, RIGHT + } + + /** + * Determines how a measurement value is interpreted + */ + public enum GridValueType { + /** + * The value is a constant pixel size + */ + CONSTANT, + /** + * The Value is relative to the parent size + */ + PERCENTAGE, + /** + * The value will be set to fill up the remaining space (i.e. when this is applied to a width of a row element, + * a {@link #FILL}-type may be used to right align (FILL - CONSTANT) or center (FILL - CONSTANT - FILL) elements. + */ + FILL, + /** + * Calculate size based on child-elements + */ + INHERIT + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridMessageCell.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridMessageCell.java new file mode 100644 index 00000000..2c31f73a --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridMessageCell.java @@ -0,0 +1,75 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.MultiLineLabel; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import java.util.List; + +@Environment(EnvType.CLIENT) +public class GridMessageCell extends GridCell { + private final Font font; + private Component text; + private MultiLineLabel lastLabel; + private GridTransform lastTransform; + + GridMessageCell(double width, + GridLayout.GridValueType widthType, + GridLayout.Alignment contentAlignment, + Font font, + Component text) { + this(width, widthType, contentAlignment, font, text, GridLayout.COLOR_WHITE); + } + + GridMessageCell(double width, + GridLayout.GridValueType widthType, + GridLayout.Alignment contentAlignment, + Font font, + Component text, + int color) { + super(width, -1, widthType, null, null); + this.font = font; + this.text = text; + + customRender = (poseStack, transform, context) -> { + //MultiLineLabel label = (MultiLineLabel) context; + if (contentAlignment == GridLayout.Alignment.CENTER) { + lastLabel.renderCentered(poseStack, + transform.width / 2 + transform.left, + transform.top, + font.lineHeight, + color); + } else if (contentAlignment == GridLayout.Alignment.LEFT) { + lastLabel.renderLeftAligned(poseStack, transform.left, transform.top, font.lineHeight, color); + } + }; + } + + public void setText(Component text) { + this.text = text; + if (lastTransform != null) { + create(lastTransform); + } + } + + private MultiLineLabel getLabel(GridTransform transform) { + return lastLabel; + } + + protected void create(GridTransform transform) { + this.lastTransform = transform; + this.lastLabel = MultiLineLabel.create(font, text, transform.width); + } + + @Override + protected GridElement buildElementAt(int left, int top, int width, List collector) { + create(new GridTransform(left, top, width, 0)); + int promptLines = this.lastLabel.getLineCount() + 1; + int height = promptLines * 9; + + return new GridElement(left, top, width, height, this::getLabel, customRender); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridRow.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridRow.java new file mode 100644 index 00000000..2838ab45 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridRow.java @@ -0,0 +1,402 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.Button.OnPress; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +@Environment(EnvType.CLIENT) +public class GridRow extends GridContainer { + public final GridLayout.VerticalAlignment alignment; + + GridRow(double width) { + this(width, GridLayout.VerticalAlignment.TOP); + } + + GridRow(double width, GridLayout.GridValueType widthType) { + this(width, widthType, GridLayout.VerticalAlignment.CENTER); + } + + GridRow(double width, GridLayout.VerticalAlignment alignment) { + super(width); + this.alignment = alignment; + } + + GridRow(double width, GridLayout.GridValueType widthType, GridLayout.VerticalAlignment alignment) { + super(width, widthType); + this.alignment = alignment; + } + + public GridColumn addColumn(double width, GridLayout.GridValueType widthType) { + GridColumn cell = new GridColumn(width, widthType); + this.cells.add(cell); + return cell; + } + + public GridCell addComponent(double height, Function componentPlacer) { + return addComponent(1.0, GridLayout.GridValueType.PERCENTAGE, height, componentPlacer); + } + + public GridCell addComponent(double width, + GridLayout.GridValueType widthType, + double height, + Function componentPlacer) { + GridCell cell = new GridCell(width, height, widthType, componentPlacer, null); + this.cells.add(cell); + return cell; + } + + + public GridCell addButton(Component text, double height, OnPress onPress) { + return addButton(text, 1.0, GridLayout.GridValueType.PERCENTAGE, height, onPress); + } + + public GridCell addButton(Component text, float alpha, double height, OnPress onPress) { + return addButton(text, alpha, 1.0, GridLayout.GridValueType.PERCENTAGE, height, onPress); + } + + public GridCell addButton(Component text, double height, Font font, OnPress onPress) { + return addButton(text, 1.0f, height, font, onPress); + } + + public GridCell addButton(Component text, float alpha, double height, Font font, OnPress onPress) { + final int width = font.width(text.getVisualOrderText()) + 24; + return addButton(text, alpha, width, GridLayout.GridValueType.CONSTANT, height, onPress); + } + + public GridCell addButton(Component text, + double width, + GridLayout.GridValueType widthType, + double height, + OnPress onPress) { + return addButton(text, 1.0f, width, widthType, height, onPress); + } + + public GridCell addButton(Component text, + float alpha, + double width, + GridLayout.GridValueType widthType, + double height, + OnPress onPress) { + GridCell cell = new GridCell(width, height, widthType, (transform) -> { + Button customButton = new Button(transform.left, + transform.top, + transform.width, + transform.height, + text, + onPress); + customButton.setAlpha(alpha); + return customButton; + }, null); + this.cells.add(cell); + return cell; + } + + public GridCheckboxCell addCheckbox(Component text, boolean checked, Font font, Consumer onChange) { + final int width = font.width(text.getVisualOrderText()) + 24 + 2 * 12; + + GridCheckboxCell cell = new GridCheckboxCell(text, checked, 1.0f, width, widthType, 20, onChange); + this.cells.add(cell); + return cell; + } + + public GridCheckboxCell addCheckbox(Component text, boolean checked, int height) { + return addCheckbox(text, checked, 1.0f, height); + } + + public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, int height) { + return addCheckbox(text, checked, alpha, 1.0, GridLayout.GridValueType.PERCENTAGE, height); + } + + public GridCheckboxCell addCheckbox(Component text, boolean checked, int height, Font font) { + return addCheckbox(text, checked, 1.0f, height, font); + } + + public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, int height, Font font) { + final int width = font.width(text.getVisualOrderText()) + 24 + 2 * 12; + return addCheckbox(text, checked, alpha, width, GridLayout.GridValueType.CONSTANT, height); + } + + public GridCheckboxCell addCheckbox(Component text, + boolean checked, + double width, + GridLayout.GridValueType widthType, + int height) { + return addCheckbox(text, checked, 1.0f, width, widthType, height); + } + + public GridCheckboxCell addCheckbox(Component text, + boolean checked, + float alpha, + double width, + GridLayout.GridValueType widthType, + int height) { + GridCheckboxCell cell = new GridCheckboxCell(text, checked, alpha, width, widthType, height); + this.cells.add(cell); + return cell; + } + + public GridCustomRenderCell addCustomRender(GridCustomRenderCell cell) { + this.cells.add(cell); + return cell; + } + + public GridCell addImage(ResourceLocation location, int width, int height) { + return addImage(location, 1.0f, width, height); + } + + public GridCell addImage(ResourceLocation location, float alpha, int width, int height) { + return addImage(location, + alpha, + width, + GridLayout.GridValueType.CONSTANT, + height, + 0, + 0, + width, + height, + width, + height); + } + + public GridCell addImage(ResourceLocation location, + double width, + GridLayout.GridValueType widthType, + int height, + int resourceWidth, + int resourceHeight) { + return addImage(location, 1.0f, width, widthType, height, resourceWidth, resourceHeight); + } + + public GridCell addImage(ResourceLocation location, + float alpha, + double width, + GridLayout.GridValueType widthType, + int height, + int resourceWidth, + int resourceHeight) { + return addImage(location, + alpha, + width, + widthType, + height, + 0, + 0, + resourceWidth, + resourceWidth, + resourceWidth, + resourceHeight); + } + + public GridCell addImage(ResourceLocation location, + double width, + GridLayout.GridValueType widthType, + int height, + int uvLeft, + int uvTop, + int uvWidth, + int uvHeight, + int resourceWidth, + int resourceHeight) { + return addImage(location, + 1.0f, + width, + widthType, + height, + uvLeft, + uvTop, + uvWidth, + uvHeight, + resourceWidth, + resourceHeight); + } + + public GridCell addImage(ResourceLocation location, + float alpha, + double width, + GridLayout.GridValueType widthType, + int height, + int uvLeft, + int uvTop, + int uvWidth, + int uvHeight, + int resourceWidth, + int resourceHeight) { + GridCell cell = new GridImageCell(location, + width, + widthType, + height, + alpha, + uvLeft, + uvTop, + uvWidth, + uvHeight, + resourceWidth, + resourceHeight); + this.cells.add(cell); + return cell; + } + + + public GridColumn addFiller() { + return addFiller(1); + } + + public GridColumn addFiller(float portion) { + GridColumn cell = new GridColumn(portion, GridLayout.GridValueType.FILL); + this.cells.add(cell); + return cell; + } + + public void addSpacer() { + addSpacer(12); + } + + public void addSpacer(int width) { + GridCell cell = new GridCell(width, 0, GridLayout.GridValueType.CONSTANT, null, null); + this.cells.add(cell); + } + + + public GridMessageCell addMessage(Component text, Font font, GridLayout.Alignment contentAlignment) { + return addMessage(text, font, GridLayout.COLOR_WHITE, contentAlignment); + } + + public GridMessageCell addMessage(Component text, Font font, int color, GridLayout.Alignment contentAlignment) { + return addMessage(text, 1.0, GridLayout.GridValueType.PERCENTAGE, font, color, contentAlignment); + } + + public GridMessageCell addMessage(Component text, + double width, + GridLayout.GridValueType widthType, + Font font, + GridLayout.Alignment contentAlignment) { + return addMessage(text, width, widthType, font, GridLayout.COLOR_WHITE, contentAlignment); + } + + public GridMessageCell addMessage(Component text, + double width, + GridLayout.GridValueType widthType, + Font font, + int color, + GridLayout.Alignment contentAlignment) { + GridMessageCell cell = new GridMessageCell(width, widthType, contentAlignment, font, text, color); + this.cells.add(cell); + return cell; + } + + public GridStringCell addString(Component text, GridScreen parent) { + return this.addString(text, GridLayout.COLOR_WHITE, parent); + } + + + public GridStringCell addString(Component text, int color, GridScreen parent) { + final int width = parent.getWidth(text); + return this.addString(text, + width, + GridLayout.GridValueType.CONSTANT, + color, + GridLayout.Alignment.CENTER, + parent); + } + + public GridStringCell addString(Component text, GridLayout.Alignment contentAlignment, GridScreen parent) { + return this.addString(text, GridLayout.COLOR_WHITE, contentAlignment, parent); + } + + public GridStringCell addString(Component text, + int color, + GridLayout.Alignment contentAlignment, + GridScreen parent) { + return this.addString(text, 1.0, GridLayout.GridValueType.PERCENTAGE, color, contentAlignment, parent); + } + + public GridStringCell addString(Component text, + double width, + GridLayout.GridValueType widthType, + GridLayout.Alignment contentAlignment, + GridScreen parent) { + return addString(text, width, widthType, GridLayout.COLOR_WHITE, contentAlignment, parent); + } + + public GridStringCell addString(Component text, + double width, + GridLayout.GridValueType widthType, + int color, + GridLayout.Alignment contentAlignment, + GridScreen parent) { + GridStringCell cell = new GridStringCell(width, + widthType, + parent.getFont().lineHeight, + contentAlignment, + parent, + text, + color); + this.cells.add(cell); + return cell; + } + + @Override + protected GridElement buildElementAt(int inLeft, int top, int width, final List collector) { + int height = 0; + int left = inLeft; + if (widthType == GridLayout.GridValueType.INHERIT) { + final int originalWidth = width; + width = cells.stream() + .filter(row -> row.widthType == GridLayout.GridValueType.CONSTANT || row.widthType == GridLayout.GridValueType.INHERIT) + .map(row -> row.buildElement(0, 0, 1, 0, 0, null).width) + .reduce(0, (p, c) -> p + c); + } + + final int inheritedWidth = width; + final int fixedWidth = cells.stream() + .filter(col -> col.widthType != GridLayout.GridValueType.FILL) + .map(col -> col.calculateWidth(inheritedWidth)) + .reduce(0, (p, c) -> p + c); + final float autoWidthSum = cells.stream() + .filter(col -> col.widthType == GridLayout.GridValueType.FILL) + .map(col -> col.width) + .reduce(0.0f, (p, c) -> p + c); + final int autoWidth = width - fixedWidth; + + if (alignment == GridLayout.VerticalAlignment.TOP) { + for (GridCellDefinition col : cells) { + GridElement element = col.buildElement(width, autoWidth, autoWidthSum, left, top, collector); + left += element.width; + height = Math.max(height, element.height); + } + } else { + //first iteration will collect heights, second one will transform top position for alignment + Map cache = new HashMap<>(); + for (GridCellDefinition col : cells) { + GridElement element = col.buildElement(width, autoWidth, autoWidthSum, left, top, null); + left += element.width; + height = Math.max(height, element.height); + cache.put(col, element); + } + + left = inLeft; + for (GridCellDefinition col : cells) { + GridElement element = cache.get(col); + final int topOffset = (alignment == GridLayout.VerticalAlignment.BOTTOM) + ? (height - element.height) + : (height - element.height) >> 1; + element = col.buildElement(width, autoWidth, autoWidthSum, left, top + topOffset, collector); + left += element.width; + } + } + + + return new GridElement(inLeft, top, width, height); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridScreen.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridScreen.java new file mode 100644 index 00000000..6daf4bce --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridScreen.java @@ -0,0 +1,266 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Widget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import org.betterx.bclib.gui.gridlayout.GridLayout.Alignment; + +import org.jetbrains.annotations.Nullable; + + +@Environment(EnvType.CLIENT) +public abstract class GridScreen extends Screen { + protected GridLayout grid = null; + public final int topPadding; + public final int sidePadding; + public final boolean centerVertically; + @Nullable + public final Screen parent; + + protected int scrollPos = 0; + + public GridScreen(Component title) { + this(null, title); + } + + public GridScreen(@Nullable Screen parent, Component title) { + this(parent, title, 0, true); + } + + public GridScreen(Component title, int topPadding, boolean centerVertically) { + this(null, title, topPadding, 20, centerVertically); + } + + public GridScreen(@Nullable Screen parent, Component title, int topPadding, boolean centerVertically) { + this(parent, title, topPadding, 20, centerVertically); + } + + public GridScreen(Component title, int topPadding, int sidePadding, boolean centerVertically) { + this(null, title, topPadding, sidePadding, centerVertically); + } + + public GridScreen(@Nullable Screen parent, + Component title, + int topPadding, + int sidePadding, + boolean centerVertically) { + super(title); + + this.parent = parent; + this.topPadding = topPadding; + this.sidePadding = sidePadding; + this.centerVertically = centerVertically; + } + + @Override + public void onClose() { + this.minecraft.setScreen(parent); + } + + public Font getFont() { + return this.font; + } + + @Override + public boolean isPauseScreen() { + return true; + } + + @Override + public T addRenderableWidget(T guiEventListener) { + return super.addRenderableWidget(guiEventListener); + } + + protected void addTitle() { + grid.addRow().addString(this.title, Alignment.CENTER, this); + grid.addSpacerRow(15); + } + + final protected void init() { + super.init(); + this.grid = new GridLayout(this, this.topPadding, this.sidePadding, this.centerVertically); + + addTitle(); + + initLayout(); + grid.finalizeLayout(); + } + + protected abstract void initLayout(); + + protected void renderScreen(PoseStack poseStack, int i, int j, float f) { + super.render(poseStack, i, j, f); + } + + public void render(PoseStack poseStack, int i, int j, float f) { + this.renderDirtBackground(i); + renderGrid(poseStack); + super.render(poseStack, i, j, f); + } + + protected void renderGrid(PoseStack poseStack) { + if (grid != null) { + if (isScrollable()) { + for (var item : grid.movableWidgets) { + item.first.y = item.second + scrollPos; + } + + renderScroll(poseStack); + + poseStack.pushPose(); + poseStack.translate(0, scrollPos, 0); + grid.render(poseStack); + poseStack.popPose(); + } else { + grid.render(poseStack); + } + } + } + + public static int getWidth(Component text, Font font) { + return font.width(text.getVisualOrderText()); + } + + public int getWidth(Component text) { + return getWidth(text, getFont()); + } + + public void setScrollPos(int sp) { + scrollPos = Math.max(getMaxScrollPos(), Math.min(0, sp)); + } + + public int getScrollPos() { + return scrollPos; + } + + public int getScrollHeight() { + if (grid != null) return grid.getHeight() + topPadding; + return height; + } + + public int getMaxScrollPos() { + return height - (getScrollHeight() + topPadding); + } + + public boolean isScrollable() { + return height < getScrollHeight(); + } + + public boolean isMouseOverScroller(double x, double y) { + return y >= 0 && y <= height && x >= width - SCROLLER_WIDTH && x <= width; + } + + private boolean scrolling = false; + + protected void updateScrollingState(double x, double y, int i) { + this.scrolling = i == 0 && x >= width - SCROLLER_WIDTH && x < width; + } + + private static final int SCROLLER_WIDTH = 6; + + private void renderScroll(PoseStack poseStack) { + final int y1 = height; + final int y0 = 0; + final int yd = y1 - y0; + final int maxPosition = getScrollHeight() + topPadding; + + final int x0 = width - SCROLLER_WIDTH; + final int x1 = width; + + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder bufferBuilder = tesselator.getBuilder(); + RenderSystem.disableTexture(); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + int widgetHeight = (int) ((float) (yd * yd) / (float) maxPosition); + widgetHeight = Mth.clamp(widgetHeight, 32, yd - 8); + float relPos = (float) this.getScrollPos() / this.getMaxScrollPos(); + int top = (int) (relPos * (yd - widgetHeight)) + y0; + if (top < y0) { + top = y0; + } + + bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + + //scroller background + bufferBuilder.vertex(x0, y1, 0.0D).color(0, 0, 0, 255).endVertex(); + bufferBuilder.vertex(x1, y1, 0.0D).color(0, 0, 0, 255).endVertex(); + bufferBuilder.vertex(x1, y0, 0.0D).color(0, 0, 0, 255).endVertex(); + bufferBuilder.vertex(x0, y0, 0.0D).color(0, 0, 0, 255).endVertex(); + + //scroll widget shadow + bufferBuilder.vertex(x0, top + widgetHeight, 0.0D).color(128, 128, 128, 255).endVertex(); + bufferBuilder.vertex(x1, top + widgetHeight, 0.0D).color(128, 128, 128, 255).endVertex(); + bufferBuilder.vertex(x1, top, 0.0D).color(128, 128, 128, 255).endVertex(); + bufferBuilder.vertex(x0, top, 0.0D).color(128, 128, 128, 255).endVertex(); + + //scroll widget + bufferBuilder.vertex(x0, top + widgetHeight - 1, 0.0D) + .color(192, 192, 192, 255) + .endVertex(); + bufferBuilder.vertex(x1 - 1, top + widgetHeight - 1, 0.0D) + .color(192, 192, 192, 255) + .endVertex(); + bufferBuilder.vertex(x1 - 1, top, 0.0D).color(192, 192, 192, 255).endVertex(); + bufferBuilder.vertex(x0, top, 0.0D).color(192, 192, 192, 255).endVertex(); + tesselator.end(); + } + + public boolean mouseClicked(double x, double y, int i) { + this.updateScrollingState(x, y, i); + if (this.scrolling) { + return true; + } else { + return super.mouseClicked(x, y, i); + } + } + + public boolean mouseDragged(double xAbs, double yAbs, int i, double dX, double dY) { + if (super.mouseDragged(xAbs, yAbs, i, dX, dY)) { + return true; + } else if (i == 0 && this.scrolling) { + if (yAbs < 0) { + this.setScrollPos(0); + } else if (yAbs > height) { + this.setScrollPos(this.getMaxScrollPos()); + } else { + this.setScrollPos((int) (this.getScrollPos() - dY * 2)); + } + + return true; + } else { + return false; + } + } + + public boolean mouseScrolled(double d, double e, double f) { + if (isScrollable()) { + setScrollPos((int) (scrollPos + f * 10)); + } + return true; + } + + public boolean keyPressed(int keyCode, int j, int k) { + if (super.keyPressed(keyCode, j, k)) { + return true; + } else if (keyCode == 264) { + this.mouseScrolled(0, -1.0f, 0); + return true; + } else if (keyCode == 265) { + this.mouseScrolled(0, 1.0, 0); + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridStringCell.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridStringCell.java new file mode 100644 index 00000000..5ef4a9dc --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridStringCell.java @@ -0,0 +1,52 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.gridlayout.GridLayout.Alignment; +import org.betterx.bclib.gui.gridlayout.GridLayout.GridValueType; + +@Environment(EnvType.CLIENT) +public class GridStringCell extends GridCell { + private Component text; + + GridStringCell(double width, + GridValueType widthType, + int height, + Alignment contentAlignment, + GridScreen parent, + Component text) { + this(width, widthType, height, contentAlignment, parent, text, GridLayout.COLOR_WHITE); + + } + + GridStringCell(double width, + GridValueType widthType, + int height, + Alignment contentAlignment, + GridScreen parent, + Component text, + int color) { + super(width, height, widthType, null, null); + this.text = text; + this.customRender = (poseStack, transform, context) -> { + if (contentAlignment == Alignment.CENTER) { + GuiComponent.drawCenteredString(poseStack, + parent.getFont(), + this.text, + transform.width / 2 + transform.left, + transform.top, + color); + } else if (contentAlignment == Alignment.LEFT) { + GuiComponent.drawString(poseStack, parent.getFont(), this.text, transform.left, transform.top, color); + } + }; + } + + public void setText(Component newText) { + this.text = newText; + } +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridTransform.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridTransform.java new file mode 100644 index 00000000..efd8dd28 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridTransform.java @@ -0,0 +1,25 @@ +package org.betterx.bclib.gui.gridlayout; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class GridTransform { + public final int left; + public final int top; + public final int width; + public final int height; + + GridTransform(int left, int top, int width, int height) { + this.left = left; + this.top = top; + this.width = width; + this.height = height; + } + + @Override + public String toString() { + return "{" + "left=" + left + ", top=" + top + ", width=" + width + ", height=" + height + '}'; + } + +} diff --git a/src/main/java/org/betterx/bclib/gui/gridlayout/GridWidgetWithEnabledState.java b/src/main/java/org/betterx/bclib/gui/gridlayout/GridWidgetWithEnabledState.java new file mode 100644 index 00000000..f5ac3741 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/gridlayout/GridWidgetWithEnabledState.java @@ -0,0 +1,6 @@ +package org.betterx.bclib.gui.gridlayout; + +public interface GridWidgetWithEnabledState { + boolean isEnabled(); + void setEnabled(boolean enabled); +} diff --git a/src/main/java/org/betterx/bclib/gui/modmenu/EntryPoint.java b/src/main/java/org/betterx/bclib/gui/modmenu/EntryPoint.java new file mode 100644 index 00000000..9ae352af --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/modmenu/EntryPoint.java @@ -0,0 +1,12 @@ +package org.betterx.bclib.gui.modmenu; + +import org.betterx.bclib.integration.modmenu.ModMenuIntegration; + +@Deprecated() +public class EntryPoint extends ModMenuIntegration { + public static final Object entrypointObject = createEntrypoint(new EntryPoint()); + + public EntryPoint() { + super(MainScreen::new); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/modmenu/MainScreen.java b/src/main/java/org/betterx/bclib/gui/modmenu/MainScreen.java new file mode 100644 index 00000000..9fa75b46 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/modmenu/MainScreen.java @@ -0,0 +1,102 @@ +package org.betterx.bclib.gui.modmenu; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import org.betterx.bclib.config.ConfigKeeper; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.config.NamedPathConfig; +import org.betterx.bclib.config.NamedPathConfig.ConfigTokenDescription; +import org.betterx.bclib.config.NamedPathConfig.DependendConfigToken; +import org.betterx.bclib.gui.gridlayout.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + +public class MainScreen extends GridScreen { + + public MainScreen(@Nullable Screen parent) { + super(parent, Component.translatable("title.bclib.modmenu.main"), 10, false); + } + + protected Component getComponent(NamedPathConfig config, ConfigTokenDescription option, String type) { + return Component.translatable(type + ".config." + config.configID + option.getPath()); + } + + Map> dependentWidgets = new HashMap<>(); + + protected void updateEnabledState() { + dependentWidgets.forEach((cb, supl) -> cb.setEnabled(supl.get())); + } + + @SuppressWarnings("unchecked") + protected void addRow(GridColumn grid, NamedPathConfig config, ConfigTokenDescription option) { + if (ConfigKeeper.BooleanEntry.class.isAssignableFrom(option.token.type)) { + addCheckbox(grid, config, (ConfigTokenDescription) option); + } + + grid.addSpacerRow(2); + } + + + protected void addCheckbox(GridColumn grid, NamedPathConfig config, ConfigTokenDescription option) { + if (option.topPadding > 0) { + grid.addSpacerRow(option.topPadding); + } + GridRow row = grid.addRow(); + if (option.leftPadding > 0) { + row.addSpacer(option.leftPadding); + } + GridCheckboxCell cb = row.addCheckbox(getComponent(config, option, "title"), + config.getRaw(option.token), + font, + (state) -> { + config.set(option.token, state); + updateEnabledState(); + }); + + if (option.token instanceof DependendConfigToken) { + dependentWidgets.put(cb, () -> option.token.dependenciesTrue(config)); + cb.setEnabled(option.token.dependenciesTrue(config)); + } + } + + @Override + public boolean shouldCloseOnEsc() { + return false; + } + + @Override + protected void initLayout() { + final int BUTTON_HEIGHT = 20; + + Configs.GENERATOR_CONFIG.getAllOptions() + .stream() + .filter(o -> !o.hidden) + .forEach(o -> addRow(grid, Configs.GENERATOR_CONFIG, o)); + grid.addSpacerRow(12); + Configs.MAIN_CONFIG.getAllOptions() + .stream() + .filter(o -> !o.hidden) + .forEach(o -> addRow(grid, Configs.MAIN_CONFIG, o)); + grid.addSpacerRow(12); + Configs.CLIENT_CONFIG.getAllOptions() + .stream() + .filter(o -> !o.hidden) + .forEach(o -> addRow(grid, Configs.CLIENT_CONFIG, o)); + + grid.addSpacerRow(15); + GridRow row = grid.addRow(); + row.addFiller(); + row.addButton(CommonComponents.GUI_DONE, BUTTON_HEIGHT, font, (button) -> { + Configs.CLIENT_CONFIG.saveChanges(); + Configs.GENERATOR_CONFIG.saveChanges(); + Configs.MAIN_CONFIG.saveChanges(); + onClose(); + }); + grid.addSpacerRow(10); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/AtomicProgressListener.java b/src/main/java/org/betterx/bclib/gui/screens/AtomicProgressListener.java new file mode 100644 index 00000000..e0203fa1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/AtomicProgressListener.java @@ -0,0 +1,10 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.network.chat.Component; + +public interface AtomicProgressListener { + void incAtomic(int maxProgress); + void resetAtomic(); + void stop(); + void progressStage(Component component); +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/BCLibScreen.java b/src/main/java/org/betterx/bclib/gui/screens/BCLibScreen.java new file mode 100644 index 00000000..b9798025 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/BCLibScreen.java @@ -0,0 +1,59 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.gui.gridlayout.GridLayout; +import org.betterx.bclib.gui.gridlayout.GridRow; +import org.betterx.bclib.gui.gridlayout.GridScreen; + +import org.jetbrains.annotations.Nullable; + +@Environment(EnvType.CLIENT) +abstract class BCLibScreen extends GridScreen { + static final ResourceLocation BCLIB_LOGO_LOCATION = new ResourceLocation(BCLib.MOD_ID, "icon.png"); + + public BCLibScreen(Component title) { + super(title); + } + + public BCLibScreen(@Nullable Screen parent, Component title) { + super(parent, title); + } + + public BCLibScreen(Component title, int topPadding, boolean centerVertically) { + super(title, topPadding, 20, centerVertically); + } + + public BCLibScreen(@Nullable Screen parent, Component title, int topPadding, boolean centerVertically) { + super(parent, title, topPadding, centerVertically); + } + + public BCLibScreen(Component title, int topPadding, int sidePadding, boolean centerVertically) { + super(title, topPadding, sidePadding, centerVertically); + } + + public BCLibScreen(@Nullable Screen parent, + Component title, + int topPadding, + int sidePadding, + boolean centerVertically) { + super(parent, title, topPadding, sidePadding, centerVertically); + } + + + protected void addTitle() { + GridRow row = grid.addRow(GridLayout.VerticalAlignment.CENTER); + row.addFiller(); + row.addImage(BCLIB_LOGO_LOCATION, 24, GridLayout.GridValueType.CONSTANT, 24, 512, 512); + row.addSpacer(4); + row.addString(this.title, this); + row.addFiller(); + grid.addSpacerRow(15); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/ConfirmFixScreen.java b/src/main/java/org/betterx/bclib/gui/screens/ConfirmFixScreen.java new file mode 100644 index 00000000..c3dc4c53 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/ConfirmFixScreen.java @@ -0,0 +1,72 @@ +package org.betterx.bclib.gui.screens; + + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.gridlayout.GridCheckboxCell; +import org.betterx.bclib.gui.gridlayout.GridLayout; +import org.betterx.bclib.gui.gridlayout.GridRow; + +import org.jetbrains.annotations.Nullable; + +@Environment(EnvType.CLIENT) +public class ConfirmFixScreen extends BCLibScreen { + protected final ConfirmFixScreen.Listener listener; + private final Component description; + protected int id; + + public ConfirmFixScreen(@Nullable Screen parent, ConfirmFixScreen.Listener listener) { + super(parent, Component.translatable("bclib.datafixer.backupWarning.title")); + this.listener = listener; + + this.description = Component.translatable("bclib.datafixer.backupWarning.message"); + } + + protected void initLayout() { + final int BUTTON_HEIGHT = 20; + + grid.addRow().addMessage(this.description, this.font, GridLayout.Alignment.CENTER); + grid.addSpacerRow(); + + GridRow row = grid.addRow(); + GridCheckboxCell backup = row.addCheckbox(Component.translatable("bclib.datafixer.backupWarning.backup"), + true, + BUTTON_HEIGHT, + this.font); + + grid.addSpacerRow(10); + + row = grid.addRow(); + GridCheckboxCell fix = row.addCheckbox(Component.translatable("bclib.datafixer.backupWarning.fix"), + true, + BUTTON_HEIGHT, + this.font); + + grid.addSpacerRow(20); + + row = grid.addRow(); + row.addFiller(); + row.addButton(CommonComponents.GUI_CANCEL, BUTTON_HEIGHT, this.font, (button) -> { + onClose(); + }); + row.addSpacer(); + row.addButton(CommonComponents.GUI_PROCEED, BUTTON_HEIGHT, this.font, (button) -> { + this.listener.proceed(backup.isChecked(), fix.isChecked()); + }); + row.addFiller(); + } + + public boolean shouldCloseOnEsc() { + return true; + } + + @Environment(EnvType.CLIENT) + public interface Listener { + void proceed(boolean createBackup, boolean applyPatches); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/ConfirmRestartScreen.java b/src/main/java/org/betterx/bclib/gui/screens/ConfirmRestartScreen.java new file mode 100644 index 00000000..a55cfb05 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/ConfirmRestartScreen.java @@ -0,0 +1,52 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.gridlayout.GridLayout.Alignment; +import org.betterx.bclib.gui.gridlayout.GridRow; + + +@Environment(EnvType.CLIENT) +public class ConfirmRestartScreen extends BCLibScreen { + private final Component description; + private final ConfirmRestartScreen.Listener listener; + + public ConfirmRestartScreen(ConfirmRestartScreen.Listener listener) { + this(listener, null); + } + + public ConfirmRestartScreen(ConfirmRestartScreen.Listener listener, Component message) { + super(Component.translatable("title.bclib.confirmrestart")); + + this.description = message == null ? Component.translatable("message.bclib.confirmrestart") : message; + this.listener = listener; + } + + protected void initLayout() { + final int BUTTON_HEIGHT = 20; + + grid.addRow().addMessage(this.description, this.font, Alignment.CENTER); + + grid.addSpacerRow(); + + GridRow row = grid.addRow(); + row.addFiller(); + row.addButton(CommonComponents.GUI_PROCEED, BUTTON_HEIGHT, font, (button) -> { + listener.proceed(); + }); + row.addFiller(); + } + + public boolean shouldCloseOnEsc() { + return false; + } + + @Environment(EnvType.CLIENT) + public interface Listener { + void proceed(); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/LevelFixErrorScreen.java b/src/main/java/org/betterx/bclib/gui/screens/LevelFixErrorScreen.java new file mode 100644 index 00000000..127c17e4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/LevelFixErrorScreen.java @@ -0,0 +1,62 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.gridlayout.GridColumn; +import org.betterx.bclib.gui.gridlayout.GridLayout; +import org.betterx.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, Component.translatable("title.bclib.datafixer.error"), 10, true); + this.errors = errors; + this.onContinue = onContinue; + } + + @Override + protected void initLayout() { + grid.addSpacerRow(); + grid.addRow() + .addMessage(Component.translatable("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) { + Component dash = Component.literal("-"); + row = col.addRow(); + row.addString(dash, this); + + row.addSpacer(4); + row.addString(Component.literal(error), this); + } + + grid.addSpacerRow(8); + row = grid.addRow(); + row.addFiller(); + row.addButton(Component.translatable("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); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/ModListScreen.java b/src/main/java/org/betterx/bclib/gui/screens/ModListScreen.java new file mode 100644 index 00000000..5ff82d27 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/ModListScreen.java @@ -0,0 +1,256 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.metadata.ModEnvironment; + +import org.betterx.bclib.api.dataexchange.handler.autosync.HelloClient; +import org.betterx.bclib.gui.gridlayout.GridColumn; +import org.betterx.bclib.gui.gridlayout.GridLayout; +import org.betterx.bclib.gui.gridlayout.GridRow; +import org.betterx.bclib.gui.gridlayout.GridScreen; +import org.betterx.bclib.util.ModUtil; +import org.betterx.bclib.util.PathUtil; +import org.betterx.bclib.util.Triple; + +import java.util.*; +import java.util.stream.Collectors; + +@Environment(EnvType.CLIENT) +public class ModListScreen extends BCLibScreen { + + private final List mods; + private final HelloClient.IServerModMap serverInfo; + private final Component description; + private final Component buttonTitle; + + private static List extractModList(Map mods) { + List list = new LinkedList(); + ModUtil.getMods().forEach((id, info) -> list.add(info)); + return list; + } + + public ModListScreen(Screen parent, + Component title, + Component description, + Map mods, + HelloClient.IServerModMap serverInfo) { + this(parent, title, description, CommonComponents.GUI_BACK, mods, serverInfo); + } + + public ModListScreen(Screen parent, + Component title, + Component description, + List mods, + HelloClient.IServerModMap serverInfo) { + this(parent, title, description, CommonComponents.GUI_BACK, mods, serverInfo); + } + + public ModListScreen(Screen parent, + Component title, + Component description, + Component button, + Map mods, + HelloClient.IServerModMap serverInfo) { + this(parent, title, description, button, extractModList(mods), serverInfo); + } + + public ModListScreen(Screen parent, + Component title, + Component description, + Component button, + List mods, + HelloClient.IServerModMap serverInfo) { + super(parent, title, 10, true); + this.mods = mods; + this.serverInfo = serverInfo; + this.description = description; + this.buttonTitle = button; + } + + public static List localMissing(HelloClient.IServerModMap serverInfo) { + return serverInfo.keySet() + .stream() + .filter(modid -> !ModUtil.getMods() + .keySet() + .stream() + .filter(mod -> mod.equals(modid)) + .findFirst() + .isPresent()).collect(Collectors.toList()); + } + + public static List serverMissing(HelloClient.IServerModMap serverInfo) { + return ModUtil.getMods().entrySet() + .stream() + .filter(entry -> entry.getValue().metadata.getEnvironment() != ModEnvironment.CLIENT) + .map(entry -> entry.getKey()) + .filter(modid -> !serverInfo.keySet() + .stream() + .filter(mod -> mod.equals(modid)) + .findFirst() + .isPresent()).collect(Collectors.toList()); + } + + + public static void addModDesc(GridColumn grid, + java.util.List mods, + HelloClient.IServerModMap serverInfo, + GridScreen parent) { + final int STATE_OK = 6; + final int STATE_SERVER_MISSING_CLIENT_MOD = 5; + final int STATE_MISSING_NOT_OFFERED = 4; + final int STATE_VERSION_CLIENT_ONLY = 7; + final int STATE_VERSION_NOT_OFFERED = 3; + final int STATE_VERSION = 2; + final int STATE_SERVER_MISSING = 1; + final int STATE_MISSING = 0; + + + List> items = new LinkedList<>(); + if (serverInfo != null) { + serverInfo.keySet() + .stream() + .filter(modid -> !mods.stream() + .filter(mod -> mod.metadata.getId().equals(modid)) + .findFirst() + .isPresent()) + .forEach(modid -> { + HelloClient.OfferedModInfo nfo = serverInfo.get(modid); + String stateString = nfo.version(); + if (nfo.size() > 0) { + stateString = "Version: " + stateString + ", Size: " + PathUtil.humanReadableFileSize(nfo.size()); + } + if (nfo.canDownload()) { + stateString += ", offered by server"; + } + + items.add(new Triple<>(modid, + nfo.canDownload() ? STATE_MISSING : STATE_MISSING_NOT_OFFERED, + stateString)); + }); + } + + mods.forEach(mod -> { + String serverVersion = null; + int serverSize = 0; + int state = STATE_OK; + if (serverInfo != null) { + final String modID = mod.metadata.getId(); + + + HelloClient.OfferedModInfo data = serverInfo.get(modID); + if (data != null) { + final String modVer = data.version(); + final int size = data.size(); + if (!modVer.equals(mod.getVersion())) { + if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT) + state = STATE_VERSION_CLIENT_ONLY; + else + state = data.canDownload() ? STATE_VERSION : STATE_VERSION_NOT_OFFERED; + serverVersion = modVer; + serverSize = size; + } + } else if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT) { + state = STATE_SERVER_MISSING_CLIENT_MOD; + } else { + state = STATE_SERVER_MISSING; + } + } + + String stateString = mod.metadata.getVersion().toString(); + if (serverVersion != null) { + stateString = "Client: " + stateString; + stateString += ", Server: " + serverVersion; + if (serverSize > 0) { + stateString += ", Size: " + PathUtil.humanReadableFileSize(serverSize); + } + } + if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT) { + stateString += ", client-only"; + } else if (mod.metadata.getEnvironment() == ModEnvironment.SERVER) { + stateString += ", server-only"; + } + items.add(new Triple<>(mod.metadata.getName(), state, stateString)); + }); + + items.stream() + .sorted(Comparator.comparing(a -> a.second + a.first.toLowerCase(Locale.ROOT))) + .forEach(t -> { + final String name = t.first; + final int state = t.second; + final String stateString = t.third; + + int color = GridLayout.COLOR_RED; + final String typeText; + if (state == STATE_VERSION || state == STATE_VERSION_NOT_OFFERED || state == STATE_VERSION_CLIENT_ONLY) { + typeText = "[VERSION]"; + if (state == STATE_VERSION_NOT_OFFERED) { + color = GridLayout.COLOR_YELLOW; + } else if (state == STATE_VERSION_CLIENT_ONLY) { + color = GridLayout.COLOR_DARK_GREEN; + } + } else if (state == STATE_MISSING || state == STATE_MISSING_NOT_OFFERED) { + typeText = "[MISSING]"; + if (state == STATE_MISSING_NOT_OFFERED) { + color = GridLayout.COLOR_YELLOW; + } + } else if (state == STATE_SERVER_MISSING || state == STATE_SERVER_MISSING_CLIENT_MOD) { + if (state == STATE_SERVER_MISSING_CLIENT_MOD) { + color = GridLayout.COLOR_CYAN; + typeText = "[OK]"; + } else { + typeText = "[NOT ON SERVER]"; + } + } else { + color = GridLayout.COLOR_DARK_GREEN; + typeText = "[OK]"; + } + Component dash = Component.literal("-"); + Component typeTextComponent = Component.literal(typeText); + GridRow row = grid.addRow(); + + row.addString(dash, parent); + + row.addSpacer(4); + row.addString(Component.literal(name), parent); + + row.addSpacer(4); + row.addString(typeTextComponent, color, parent); + + if (!stateString.isEmpty()) { + row = grid.addRow(); + row.addSpacer(4 + parent.getWidth(dash)); + row.addString(Component.literal(stateString), GridLayout.COLOR_GRAY, parent); + } + + grid.addSpacerRow(); + }); + } + + @Override + protected void initLayout() { + if (description != null) { + grid.addSpacerRow(); + grid.addRow().addMessage(description, font, GridLayout.Alignment.CENTER); + grid.addSpacerRow(8); + } + + GridRow row = grid.addRow(); + row.addSpacer(10); + GridColumn col = row.addColumn(200, GridLayout.GridValueType.CONSTANT); + addModDesc(col, mods, serverInfo, this); + + grid.addSpacerRow(8); + row = grid.addRow(); + row.addFiller(); + row.addButton(buttonTitle, 20, font, (n) -> { + onClose(); + }); + row.addFiller(); + } + +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/ProgressScreen.java b/src/main/java/org/betterx/bclib/gui/screens/ProgressScreen.java new file mode 100644 index 00000000..e5dd28b7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/ProgressScreen.java @@ -0,0 +1,193 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ProgressListener; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.gui.gridlayout.*; + +import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.Nullable; + +class ProgressLogoRender extends GridCustomRenderCell { + public static final int SIZE = 64; + public static final int LOGO_SIZE = 512; + public static final int PIXELATED_SIZE = 512; + float percentage = 0; + double time = 0; + + protected ProgressLogoRender() { + super(SIZE, GridLayout.GridValueType.CONSTANT, SIZE); + } + + @Override + public void onRender(PoseStack poseStack, GridTransform transform, Object context) { + time += 0.03; + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); + + final int yBarLocal = (int) (transform.height * percentage); + final int yBar = transform.top + yBarLocal; + + final float fScale = (float) (0.3 * ((Math.sin(time) + 1.0) * 0.5) + 0.7); + int height = (int) (transform.height * fScale); + int width = (int) (transform.width * fScale); + width -= ((transform.width - width) % 2); + height -= ((transform.height - height) % 2); + + final int yOffset = (transform.height - height) / 2; + final int xOffset = (transform.width - width) / 2; + + + final int yBarImage = Math.max(0, Math.min(height, yBarLocal - yOffset)); + final float relativeY = ((float) yBarImage / height); + + if (yBarImage > 0) { + final int uvTopLogo = (int) (relativeY * LOGO_SIZE); + RenderSystem.setShaderTexture(0, BCLibScreen.BCLIB_LOGO_LOCATION); + GuiComponent.blit(poseStack, + xOffset + transform.left, + yOffset + transform.top, + width, + yBarImage, + 0, 0, LOGO_SIZE, uvTopLogo, + LOGO_SIZE, LOGO_SIZE + ); + } + + if (yBarImage < height) { + final int uvTopPixelated = (int) (relativeY * PIXELATED_SIZE); + RenderSystem.setShaderTexture(0, ProgressScreen.BCLIB_LOGO_PIXELATED_LOCATION); + GuiComponent.blit(poseStack, + xOffset + transform.left, + yOffset + transform.top + yBarImage, + width, + height - yBarImage, + 0, uvTopPixelated, PIXELATED_SIZE, PIXELATED_SIZE - uvTopPixelated, + PIXELATED_SIZE, PIXELATED_SIZE + ); + } + + if (percentage > 0 && percentage < 1.0) { + GuiComponent.fill(poseStack, + transform.left, + yBar, + transform.left + transform.width, + yBar + 1, + 0x3FFFFFFF + ); + } + } +} + +public class ProgressScreen extends GridScreen implements ProgressListener, AtomicProgressListener { + + static final ResourceLocation BCLIB_LOGO_PIXELATED_LOCATION = new ResourceLocation(BCLib.MOD_ID, + "iconpixelated.png"); + + public ProgressScreen(@Nullable Screen parent, Component title, Component description) { + super(parent, title, 20, true); + this.description = description; + } + + + Component description; + private Component stageComponent; + private GridMessageCell stage; + private GridStringCell progress; + private ProgressLogoRender progressImage; + private int currentProgress = 0; + private AtomicInteger atomicCounter; + + @Override + public void incAtomic(int maxProgress) { + if (atomicCounter != null) { + progressStagePercentage((100 * atomicCounter.incrementAndGet()) / maxProgress); + } + } + + @Override + public void resetAtomic() { + progressStagePercentage(0); + atomicCounter = new AtomicInteger(0); + } + + public boolean shouldCloseOnEsc() { + return false; + } + + public Component getProgressComponent() { + return getProgressComponent(currentProgress); + } + + private Component getProgressComponent(int pg) { + return Component.translatable("title.bclib.progress").append(": " + pg + "%"); + } + + @Override + protected void initLayout() { + grid.addSpacerRow(); + + GridRow row = grid.addRow(GridLayout.VerticalAlignment.CENTER); + row.addFiller(); + progressImage = new ProgressLogoRender(); + progressImage.percentage = currentProgress / 100.0f; + row.addCustomRender(progressImage); + row.addSpacer(); + + int textWidth = Math.max(getWidth(description), getWidth(getProgressComponent(100))); + GridColumn textCol = row.addColumn(0, GridLayout.GridValueType.INHERIT); + textCol.addRow().addString(description, this); + textCol.addSpacerRow(); + progress = textCol.addRow() + .addString(getProgressComponent(), GridLayout.COLOR_GRAY, GridLayout.Alignment.LEFT, this); + + row.addFiller(); + + grid.addSpacerRow(20); + row = grid.addRow(); + stage = row.addMessage(stageComponent != null ? stageComponent : Component.literal(""), + font, + GridLayout.Alignment.CENTER); + } + + @Override + public void progressStartNoAbort(Component text) { + this.progressStage(text); + } + + @Override + public void progressStart(Component text) { + this.progressStage(text); + this.progressStagePercentage(0); + } + + @Override + public void progressStage(Component text) { + stageComponent = text; + if (stage != null) stage.setText(text); + } + + @Override + public void progressStagePercentage(int progress) { + if (progress != currentProgress) { + currentProgress = progress; + if (progressImage != null) progressImage.percentage = currentProgress / 100.0f; + if (this.progress != null) this.progress.setText(getProgressComponent()); + } + } + + @Override + public void stop() { + + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/SyncFilesScreen.java b/src/main/java/org/betterx/bclib/gui/screens/SyncFilesScreen.java new file mode 100644 index 00000000..f0373bba --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/SyncFilesScreen.java @@ -0,0 +1,133 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.api.dataexchange.handler.autosync.HelloClient; +import org.betterx.bclib.gui.gridlayout.GridCheckboxCell; +import org.betterx.bclib.gui.gridlayout.GridLayout; +import org.betterx.bclib.gui.gridlayout.GridRow; +import org.betterx.bclib.util.ModUtil; + +@Environment(EnvType.CLIENT) +public class SyncFilesScreen extends BCLibScreen { + private final Component description; + private final SyncFilesScreen.Listener listener; + private final boolean hasConfigFiles; + private final boolean hasFiles; + private final boolean hasMods; + private final boolean shouldDelete; + private final HelloClient.IServerModMap serverInfo; + + public SyncFilesScreen(int modFiles, + int configFiles, + int singleFiles, + int folderFiles, + int deleteFiles, + HelloClient.IServerModMap serverInfo, + Listener listener) { + super(Component.translatable("title.bclib.syncfiles")); + + this.serverInfo = serverInfo; + this.description = Component.translatable("message.bclib.syncfiles"); + this.listener = listener; + + this.hasConfigFiles = configFiles > 0; + this.hasFiles = singleFiles + folderFiles > 0; + this.hasMods = modFiles > 0; + this.shouldDelete = deleteFiles > 0; + } + + protected void initLayout() { + final int BUTTON_HEIGHT = 20; + + grid.addRow() + .addMessage(this.description, this.font, GridLayout.Alignment.CENTER); + + grid.addSpacerRow(10); + + GridRow row; + + + final GridCheckboxCell mods; + row = grid.addRow(); + mods = row.addCheckbox(Component.translatable("message.bclib.syncfiles.mods"), + hasMods, + BUTTON_HEIGHT, + this.font); + mods.setEnabled(hasMods); + + row.addSpacer(); + row.addButton(Component.translatable("title.bclib.syncfiles.modInfo"), 20, font, (button) -> { + ModListScreen scr = new ModListScreen( + this, + Component.translatable("title.bclib.syncfiles.modlist"), + Component.translatable("message.bclib.syncfiles.modlist"), + ModUtil.getMods(), + serverInfo + ); + Minecraft.getInstance().setScreen(scr); + }); + + grid.addSpacerRow(); + + + final GridCheckboxCell configs; + row = grid.addRow(); + configs = row.addCheckbox(Component.translatable("message.bclib.syncfiles.configs"), + hasConfigFiles, + BUTTON_HEIGHT, + this.font); + configs.setEnabled(hasConfigFiles); + + grid.addSpacerRow(); + + row = grid.addRow(); + + final GridCheckboxCell folder; + folder = row.addCheckbox(Component.translatable("message.bclib.syncfiles.folders"), + hasFiles, + BUTTON_HEIGHT, + this.font); + folder.setEnabled(hasFiles); + row.addSpacer(); + + GridCheckboxCell delete; + delete = row.addCheckbox(Component.translatable("message.bclib.syncfiles.delete"), + shouldDelete, + BUTTON_HEIGHT, + this.font); + delete.setEnabled(shouldDelete); + + + grid.addSpacerRow(30); + row = grid.addRow(); + row.addFiller(); + row.addButton(CommonComponents.GUI_NO, BUTTON_HEIGHT, this.font, (button) -> { + listener.proceed(false, false, false, false); + }); + row.addSpacer(); + row.addButton(CommonComponents.GUI_YES, BUTTON_HEIGHT, this.font, (button) -> { + listener.proceed( + mods.isChecked(), + configs.isChecked(), + folder.isChecked(), + delete.isChecked() + ); + }); + row.addFiller(); + } + + public boolean shouldCloseOnEsc() { + return false; + } + + @Environment(EnvType.CLIENT) + public interface Listener { + void proceed(boolean downloadMods, boolean downloadConfigs, boolean downloadFiles, boolean removeFiles); + } +} diff --git a/src/main/java/org/betterx/bclib/gui/screens/WarnBCLibVersionMismatch.java b/src/main/java/org/betterx/bclib/gui/screens/WarnBCLibVersionMismatch.java new file mode 100644 index 00000000..7ec3e49a --- /dev/null +++ b/src/main/java/org/betterx/bclib/gui/screens/WarnBCLibVersionMismatch.java @@ -0,0 +1,49 @@ +package org.betterx.bclib.gui.screens; + +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.gui.gridlayout.GridLayout; +import org.betterx.bclib.gui.gridlayout.GridRow; + +@Environment(EnvType.CLIENT) +public class WarnBCLibVersionMismatch extends BCLibScreen { + private final Component description; + private final Listener listener; + + public WarnBCLibVersionMismatch(Listener listener) { + super(Component.translatable("title.bclib.bclibmissmatch")); + + this.description = Component.translatable("message.bclib.bclibmissmatch"); + this.listener = listener; + } + + protected void initLayout() { + final int BUTTON_HEIGHT = 20; + + grid.addRow().addMessage(this.description, this.font, GridLayout.Alignment.CENTER); + grid.addSpacerRow(20); + GridRow row = grid.addRow(); + row.addFiller(); + row.addButton(CommonComponents.GUI_NO, BUTTON_HEIGHT, this.font, (button) -> { + listener.proceed(false); + }); + row.addSpacer(); + row.addButton(CommonComponents.GUI_YES, BUTTON_HEIGHT, this.font, (button) -> { + listener.proceed(true); + }); + row.addFiller(); + } + + public boolean shouldCloseOnEsc() { + return false; + } + + @Environment(EnvType.CLIENT) + public interface Listener { + void proceed(boolean download); + } +} diff --git a/src/main/java/org/betterx/bclib/integration/ModIntegration.java b/src/main/java/org/betterx/bclib/integration/ModIntegration.java new file mode 100644 index 00000000..c221a25d --- /dev/null +++ b/src/main/java/org/betterx/bclib/integration/ModIntegration.java @@ -0,0 +1,213 @@ +package org.betterx.bclib.integration; + +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; + +import net.fabricmc.loader.api.FabricLoader; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.world.features.BCLFeature; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public abstract class ModIntegration { + private final String modID; + + public void init() { + } + + public ModIntegration(String modID) { + this.modID = modID; + } + + public ResourceLocation getID(String name) { + return new ResourceLocation(modID, name); + } + + public ResourceKey getFeatureKey(String name) { + return ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, getID(name)); + } + + public Block getBlock(String name) { + return Registry.BLOCK.get(getID(name)); + } + + public Item getItem(String name) { + return Registry.ITEM.get(getID(name)); + } + + public BlockState getDefaultState(String name) { + return getBlock(name).defaultBlockState(); + } + + public ResourceKey getKey(String name) { + return ResourceKey.create(Registry.BIOME_REGISTRY, getID(name)); + } + + public boolean modIsInstalled() { + return FabricLoader.getInstance().isModLoaded(modID); + } + + public BCLFeature getFeature(String featureID, String placedFeatureID, GenerationStep.Decoration featureStep) { + ResourceLocation id = getID(featureID); + Feature feature = Registry.FEATURE.get(id); + Holder featurePlaced = BuiltinRegistries.PLACED_FEATURE.getHolder(getFeatureKey(placedFeatureID)) + .orElse(null); + return new BCLFeature(id, feature, featureStep, featurePlaced); + } + + public BCLFeature getFeature(String name, GenerationStep.Decoration featureStep) { + return getFeature(name, name, featureStep); + } + + public ConfiguredFeature getConfiguredFeature(String name) { + return BuiltinRegistries.CONFIGURED_FEATURE.get(getID(name)); + } + + public Holder getBiome(String name) { + return BuiltinRegistries.BIOME.getHolder(getKey(name)).orElseThrow(); + } + + public Class getClass(String path) { + Class cl = null; + try { + cl = Class.forName(path); + } catch (ClassNotFoundException e) { + BCLib.LOGGER.error(e.getMessage()); + if (BCLib.isDevEnvironment()) { + e.printStackTrace(); + } + } + return cl; + } + + @SuppressWarnings("unchecked") + public T getStaticFieldValue(Class cl, String name) { + if (cl != null) { + try { + Field field = cl.getDeclaredField(name); + if (field != null) { + return (T) field.get(null); + } + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + return null; + } + + public Object getFieldValue(Class cl, String name, Object classInstance) { + if (cl != null) { + try { + Field field = cl.getDeclaredField(name); + if (field != null) { + return field.get(classInstance); + } + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + return null; + } + + public Method getMethod(Class cl, String functionName, Class... args) { + if (cl != null) { + try { + return cl.getMethod(functionName, args); + } catch (NoSuchMethodException | SecurityException e) { + BCLib.LOGGER.error(e.getMessage()); + if (BCLib.isDevEnvironment()) { + e.printStackTrace(); + } + } + } + return null; + } + + public Object executeMethod(Object instance, Method method, Object... args) { + if (method != null) { + try { + return method.invoke(instance, args); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + BCLib.LOGGER.error(e.getMessage()); + if (BCLib.isDevEnvironment()) { + e.printStackTrace(); + } + } + } + return null; + } + + public Object getAndExecuteStatic(Class cl, String functionName, Object... args) { + if (cl != null) { + Class[] classes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + classes[i] = args[i].getClass(); + } + Method method = getMethod(cl, functionName, classes); + return executeMethod(null, method, args); + } + return null; + } + + @SuppressWarnings("unchecked") + public T getAndExecuteRuntime(Class cl, + Object instance, + String functionName, + Object... args) { + if (instance != null) { + Class[] classes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + classes[i] = args[i].getClass(); + } + Method method = getMethod(cl, functionName, classes); + return (T) executeMethod(instance, method, args); + } + return null; + } + + public Object newInstance(Class cl, Object... args) { + if (cl != null) { + for (Constructor constructor : cl.getConstructors()) { + if (constructor.getParameterCount() == args.length) { + try { + return constructor.newInstance(args); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | + InvocationTargetException e) { + BCLib.LOGGER.error(e.getMessage()); + if (BCLib.isDevEnvironment()) { + e.printStackTrace(); + } + } + } + } + } + return null; + } + + public TagKey getItemTag(String name) { + ResourceLocation id = getID(name); + return TagAPI.makeItemTag(id); + } + + public TagKey getBlockTag(String name) { + ResourceLocation id = getID(name); + return TagAPI.makeBlockTag(id); + } +} diff --git a/src/main/java/org/betterx/bclib/integration/modmenu/ModMenu.java b/src/main/java/org/betterx/bclib/integration/modmenu/ModMenu.java new file mode 100644 index 00000000..414862ad --- /dev/null +++ b/src/main/java/org/betterx/bclib/integration/modmenu/ModMenu.java @@ -0,0 +1,30 @@ +package org.betterx.bclib.integration.modmenu; + +import net.minecraft.client.gui.screens.Screen; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * Integration, to provide a custom Screen for ModMenu. + *

+ * This integration allows you to use ModMenu without adding a dependency to your project. If the + * Mod is installed on the Client. + *

+ * You can add a screen for your mod by calling {@link #addModMenuScreen(String, Function)} + */ +public class ModMenu { + static final Map> screen = new HashMap<>(); + + /** + * registers a ModMenu entrypoint for another Mod. For Example {@code addModMenuScreen("myMod", (parent)->new Screen(parent));} + * + * @param modID The ID of your Mod + * @param scr a function that takes a parent {@link Screen} and provides the main Screen you want + * to show with ModMenu for your Mod. + */ + public static void addModMenuScreen(String modID, Function scr) { + screen.put(modID, scr); + } +} diff --git a/src/main/java/org/betterx/bclib/integration/modmenu/ModMenuEntryPoint.java b/src/main/java/org/betterx/bclib/integration/modmenu/ModMenuEntryPoint.java new file mode 100644 index 00000000..e640fbd6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/integration/modmenu/ModMenuEntryPoint.java @@ -0,0 +1,32 @@ +package org.betterx.bclib.integration.modmenu; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import org.betterx.bclib.gui.modmenu.MainScreen; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + + +/** + * Internal class to hook into ModMenu, you should not need to use this class. If you want to register a + * ModMenu Screen for a Mod using BCLib, use {@link ModMenu#addModMenuScreen(String, Function)} + */ +public class ModMenuEntryPoint implements ModMenuApi { + @Override + public Map> getProvidedConfigScreenFactories() { + Map> copy = new HashMap<>(); + for (var entry : ModMenu.screen.entrySet()) { + copy.put(entry.getKey(), (parent) -> entry.getValue().apply(parent)); + } + return copy; + } + + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return (parent) -> new MainScreen(parent); + } + + +} diff --git a/src/main/java/org/betterx/bclib/integration/modmenu/ModMenuIntegration.java b/src/main/java/org/betterx/bclib/integration/modmenu/ModMenuIntegration.java new file mode 100644 index 00000000..77c0a1eb --- /dev/null +++ b/src/main/java/org/betterx/bclib/integration/modmenu/ModMenuIntegration.java @@ -0,0 +1,180 @@ +package org.betterx.bclib.integration.modmenu; + +import net.minecraft.client.gui.screens.Screen; + +import com.google.common.collect.ImmutableMap; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import org.betterx.bclib.integration.modmenu.ModMenuIntegration.ModMenuScreenFactory; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Map; + +@Deprecated +class ModMenuScreenFactoryImpl { + record ScreenFactoryInvocationHandler(ModMenuScreenFactory act) implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return switch (method.getName()) { + case "toString" -> ""; + case "equals" -> false; + case "hashCode" -> 0; + default -> act.create((Screen) args[0]); + }; + } + } + + public static ModMenuScreenFactory create(ModMenuScreenFactory act) { + Class iConfigScreenFactory = null; + try { + iConfigScreenFactory = Class.forName("com.terraformersmc.modmenu.api.ConfigScreenFactory"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + + Object o = Proxy.newProxyInstance( + ModMenuIntegration.class.getClassLoader(), + new Class[]{iConfigScreenFactory, ModMenuScreenFactory.class}, + new ScreenFactoryInvocationHandler(act)); + + return (ModMenuScreenFactory) o; + } +} + +@Deprecated +/** + * Integration, to provide a custom Screen for ModMenu. + *

+ * This integration allows you to use ModMenu without adding a dependency to your project. If the + * Mod is installed on the Client, and the correct ModMenu-EntryPoint is registered in your fabric.mod.json + * the screen will show up. + *

+ * You only need to subclass this class, and initialize a static Field of Type {@link ModMenuApi} using + * the {@link #createEntrypoint(ModMenuIntegration)}-Method. + *

+ * Example: + *

{@code public class ModMenu extends ModMenuIntegration {
+ *	 public static final ModMenuApiMarker entrypointObject = createEntrypoint(new EntryPoint());
+ *
+ * 		public EntryPoint() {
+ * 			super(GridScreen::new);
+ *      }
+ * }}
+ * You'd also need to add the ModMenu-Entrypoint to your fabric.mod.json: + *
"entrypoints": {
+ * 		...
+ *	 "modmenu": [ "your.mod.ModMenu::entrypointObject" ]
+ * }
+ */ +public abstract class ModMenuIntegration { + /** + * Creates a new EntryPoint Object that is accepted by ModMenu + * + * @param target The delegate Object that will receive calls from ModMenu + * @return A Proxy that conforms to the ModMenu spec + */ + public static ModMenuApi createEntrypoint(ModMenuIntegration target) { + Class iModMenuAPI; + //Class iModMenuAPIMarker = null; + try { + iModMenuAPI = Class.forName("com.terraformersmc.modmenu.api.ModMenuApi"); + //iModMenuAPIMarker = Class.forName("com.terraformersmc.modmenu.util.ModMenuApiMarker"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + + Object o = Proxy.newProxyInstance( + ModMenuIntegration.class.getClassLoader(), + new Class[]{iModMenuAPI}, + new BCLibModMenuInvocationHandler(target)); + + return (ModMenuApi) o; + } + + /** + * The factory passed to {@link #ModMenuIntegration(ModMenuScreenFactory)} + */ + protected final ModMenuScreenFactory screenFactory; + + /** + * Create a new ModMenu delegate + * + * @param screenFactory A Factory. The Factory receives the currently visible {@code parent}-Screen + * and must return a new Screen Object. + */ + public ModMenuIntegration(ModMenuScreenFactory screenFactory) { + this.screenFactory = screenFactory; + } + + /** + * A Helper class to make a BCLib-Factory conform to the ModMenu-Factory Interface. + * + * @param factory A BCLib-Type Factory, ie. {@code GridScreen::new } + * @return A ModMenu Factory for a Screen + */ + final protected ModMenuScreenFactory createFactory(ModMenuScreenFactory factory) { + return ModMenuScreenFactoryImpl.create(factory); + } + + /** + * Used to construct a new config screen instance when your mod's + * configuration button is selected on the mod menu screen. The + * screen instance parameter is the active mod menu screen. + * (Text copied from ModMenu) + * + * @return A factory for constructing config screen instances. + */ + public ModMenuScreenFactory getModConfigScreenFactory() { + return createFactory(screenFactory); + } + + /** + * Used to provide config screen factories for other mods. This takes second + * priority to a mod's own config screen factory provider. For example, if + * mod `xyz` supplies a config screen factory, mod `abc` providing a config + * screen to `xyz` will be pointless, as the one provided by `xyz` will be + * used. + *

+ * This method is NOT meant to be used to add a config screen factory to + * your own mod. + * (Text copied from ModMenu) + * + * @return a map of mod ids to screen factories. + */ + public Map getProvidedConfigScreenFactories() { + return ImmutableMap.of(); + } + + @Override + public String toString() { + return super.toString(); + } + + /** + * A Factory Interface for ModMenu-Screens + *

+ * The Interface matches {@code com.terraformersmc.modmenu.api.ConfigScreenFactory} + */ + @FunctionalInterface + public interface ModMenuScreenFactory extends ConfigScreenFactory { + } + + record BCLibModMenuInvocationHandler(ModMenuIntegration target) implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return switch (method.getName()) { + case "getModConfigScreenFactory" -> target.getModConfigScreenFactory(); + case "getProvidedConfigScreenFactories" -> target.getProvidedConfigScreenFactories(); + case "toString" -> target.toString(); + case "equals" -> target.equals(args[0]); + case "hashCode" -> target.hashCode(); + default -> null; + }; + } + } + +} diff --git a/src/main/java/org/betterx/bclib/interfaces/AnvilScreenHandlerExtended.java b/src/main/java/org/betterx/bclib/interfaces/AnvilScreenHandlerExtended.java new file mode 100644 index 00000000..b17116f2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/AnvilScreenHandlerExtended.java @@ -0,0 +1,36 @@ +package org.betterx.bclib.interfaces; + + +import org.betterx.bclib.recipes.AnvilRecipe; + +import java.util.List; + +public interface AnvilScreenHandlerExtended { + void be_updateCurrentRecipe(AnvilRecipe recipe); + + AnvilRecipe be_getCurrentRecipe(); + + List be_getRecipes(); + + default void be_nextRecipe() { + List 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 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)); + } +} diff --git a/src/main/java/org/betterx/bclib/interfaces/BiomeChunk.java b/src/main/java/org/betterx/bclib/interfaces/BiomeChunk.java new file mode 100644 index 00000000..d71cc483 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/BiomeChunk.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.interfaces; + +import org.betterx.bclib.world.generator.BiomePicker; + +public interface BiomeChunk { + void setBiome(int x, int z, BiomePicker.ActualBiome biome); + BiomePicker.ActualBiome getBiome(int x, int z); + int getSide(); +} diff --git a/src/main/java/org/betterx/bclib/interfaces/BiomeMap.java b/src/main/java/org/betterx/bclib/interfaces/BiomeMap.java new file mode 100644 index 00000000..25d0f6d4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/BiomeMap.java @@ -0,0 +1,10 @@ +package org.betterx.bclib.interfaces; + +import org.betterx.bclib.world.generator.BiomePicker; + +public interface BiomeMap { + void setChunkProcessor(TriConsumer processor); + BiomeChunk getChunk(int cx, int cz, boolean update); + BiomePicker.ActualBiome getBiome(double x, double y, double z); + void clearCache(); +} diff --git a/src/main/java/ru/bclib/interfaces/BiomeSetter.java b/src/main/java/org/betterx/bclib/interfaces/BiomeSetter.java similarity index 56% rename from src/main/java/ru/bclib/interfaces/BiomeSetter.java rename to src/main/java/org/betterx/bclib/interfaces/BiomeSetter.java index 09a8e447..eee82afa 100644 --- a/src/main/java/ru/bclib/interfaces/BiomeSetter.java +++ b/src/main/java/org/betterx/bclib/interfaces/BiomeSetter.java @@ -1,8 +1,8 @@ -package ru.bclib.interfaces; +package org.betterx.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); + void bclib_setBiome(Biome biome, BlockPos pos); } diff --git a/src/main/java/org/betterx/bclib/interfaces/BiomeSourceAccessor.java b/src/main/java/org/betterx/bclib/interfaces/BiomeSourceAccessor.java new file mode 100644 index 00000000..837af156 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/BiomeSourceAccessor.java @@ -0,0 +1,5 @@ +package org.betterx.bclib.interfaces; + +public interface BiomeSourceAccessor { + void bclRebuildFeatures(); +} diff --git a/src/main/java/org/betterx/bclib/interfaces/BlockModelProvider.java b/src/main/java/org/betterx/bclib/interfaces/BlockModelProvider.java new file mode 100644 index 00000000..9bb3fc4f --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/BlockModelProvider.java @@ -0,0 +1,50 @@ +package org.betterx.bclib.interfaces; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; + +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +public interface BlockModelProvider extends ItemModelProvider { + @Environment(EnvType.CLIENT) + default @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + Optional pattern = PatternsHelper.createBlockSimple(resourceLocation); + return ModelsHelper.fromPattern(pattern); + } + + @Environment(EnvType.CLIENT) + default UnbakedModel getModelVariant(ResourceLocation stateId, + BlockState blockState, + Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createBlockSimple(modelId); + } + + @Environment(EnvType.CLIENT) + default void registerBlockModel(ResourceLocation stateId, + ResourceLocation modelId, + BlockState blockState, + Map modelCache) { + if (!modelCache.containsKey(modelId)) { + BlockModel model = getBlockModel(stateId, blockState); + if (model != null) { + model.name = modelId.toString(); + modelCache.put(modelId, model); + } else { + BCLib.LOGGER.warning("Error loading model: {}", modelId); + } + } + } +} diff --git a/src/main/java/ru/bclib/interfaces/ChunkGeneratorAccessor.java b/src/main/java/org/betterx/bclib/interfaces/ChunkGeneratorAccessor.java similarity index 84% rename from src/main/java/ru/bclib/interfaces/ChunkGeneratorAccessor.java rename to src/main/java/org/betterx/bclib/interfaces/ChunkGeneratorAccessor.java index cb20ad10..07aea210 100644 --- a/src/main/java/ru/bclib/interfaces/ChunkGeneratorAccessor.java +++ b/src/main/java/org/betterx/bclib/interfaces/ChunkGeneratorAccessor.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.core.Registry; import net.minecraft.world.level.levelgen.structure.StructureSet; diff --git a/src/main/java/ru/bclib/interfaces/CustomColorProvider.java b/src/main/java/org/betterx/bclib/interfaces/CustomColorProvider.java similarity index 58% rename from src/main/java/ru/bclib/interfaces/CustomColorProvider.java rename to src/main/java/org/betterx/bclib/interfaces/CustomColorProvider.java index 608df477..5fff479f 100644 --- a/src/main/java/ru/bclib/interfaces/CustomColorProvider.java +++ b/src/main/java/org/betterx/bclib/interfaces/CustomColorProvider.java @@ -1,10 +1,10 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.client.color.block.BlockColor; import net.minecraft.client.color.item.ItemColor; public interface CustomColorProvider { - BlockColor getProvider(); - - ItemColor getItemProvider(); + BlockColor getProvider(); + + ItemColor getItemProvider(); } diff --git a/src/main/java/org/betterx/bclib/interfaces/CustomItemProvider.java b/src/main/java/org/betterx/bclib/interfaces/CustomItemProvider.java new file mode 100644 index 00000000..ccd59262 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/CustomItemProvider.java @@ -0,0 +1,15 @@ +package org.betterx.bclib.interfaces; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; + +public interface CustomItemProvider { + /** + * Used to replace default Block Item when block is registered. + * + * @return {@link BlockItem} + */ + BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings); +} diff --git a/src/main/java/org/betterx/bclib/interfaces/ItemModelProvider.java b/src/main/java/org/betterx/bclib/interfaces/ItemModelProvider.java new file mode 100644 index 00000000..45140c45 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/ItemModelProvider.java @@ -0,0 +1,16 @@ +package org.betterx.bclib.interfaces; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; + +public interface ItemModelProvider { + @Environment(EnvType.CLIENT) + default BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createItemModel(resourceLocation); + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/interfaces/NoiseGeneratorSettingsProvider.java b/src/main/java/org/betterx/bclib/interfaces/NoiseGeneratorSettingsProvider.java similarity index 82% rename from src/main/java/ru/bclib/interfaces/NoiseGeneratorSettingsProvider.java rename to src/main/java/org/betterx/bclib/interfaces/NoiseGeneratorSettingsProvider.java index 1d00e595..f3818e83 100644 --- a/src/main/java/ru/bclib/interfaces/NoiseGeneratorSettingsProvider.java +++ b/src/main/java/org/betterx/bclib/interfaces/NoiseGeneratorSettingsProvider.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; diff --git a/src/main/java/org/betterx/bclib/interfaces/NumericProvider.java b/src/main/java/org/betterx/bclib/interfaces/NumericProvider.java new file mode 100644 index 00000000..c6055197 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/NumericProvider.java @@ -0,0 +1,25 @@ +package org.betterx.bclib.interfaces; + +import net.minecraft.core.MappedRegistry; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.Lifecycle; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; + +import java.util.function.Function; + +public interface NumericProvider { + ResourceKey>> NUMERIC_PROVIDER_REGISTRY = ResourceKey.createRegistryKey( + BCLib.makeID("worldgen/numeric_provider")); + Registry> NUMERIC_PROVIDER = new MappedRegistry<>(NUMERIC_PROVIDER_REGISTRY, + Lifecycle.experimental(), + null); + Codec CODEC = NUMERIC_PROVIDER.byNameCodec() + .dispatch(NumericProvider::pcodec, Function.identity()); + int getNumber(SurfaceRulesContextAccessor context); + + Codec pcodec(); +} diff --git a/src/main/java/org/betterx/bclib/interfaces/PatchBiFunction.java b/src/main/java/org/betterx/bclib/interfaces/PatchBiFunction.java new file mode 100644 index 00000000..06f42303 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/PatchBiFunction.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.interfaces; + +import org.betterx.bclib.api.datafixer.MigrationProfile; +import org.betterx.bclib.api.datafixer.PatchDidiFailException; + +@FunctionalInterface +public interface PatchBiFunction { + R apply(U t, V v, MigrationProfile profile) throws PatchDidiFailException; +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/interfaces/PatchFunction.java b/src/main/java/org/betterx/bclib/interfaces/PatchFunction.java new file mode 100644 index 00000000..35a863ee --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/PatchFunction.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.interfaces; + +import org.betterx.bclib.api.datafixer.MigrationProfile; +import org.betterx.bclib.api.datafixer.PatchDidiFailException; + +@FunctionalInterface +public interface PatchFunction { + R apply(T t, MigrationProfile profile) throws PatchDidiFailException; +} diff --git a/src/main/java/org/betterx/bclib/interfaces/PostInitable.java b/src/main/java/org/betterx/bclib/interfaces/PostInitable.java new file mode 100644 index 00000000..751fe877 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/PostInitable.java @@ -0,0 +1,5 @@ +package org.betterx.bclib.interfaces; + +public interface PostInitable { + void postInit(); +} diff --git a/src/main/java/org/betterx/bclib/interfaces/RenderLayerProvider.java b/src/main/java/org/betterx/bclib/interfaces/RenderLayerProvider.java new file mode 100644 index 00000000..5fc1f2d7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/RenderLayerProvider.java @@ -0,0 +1,7 @@ +package org.betterx.bclib.interfaces; + +import org.betterx.bclib.client.render.BCLRenderLayer; + +public interface RenderLayerProvider { + BCLRenderLayer getRenderLayer(); +} diff --git a/src/main/java/ru/bclib/interfaces/SpawnRule.java b/src/main/java/org/betterx/bclib/interfaces/SpawnRule.java similarity index 50% rename from src/main/java/ru/bclib/interfaces/SpawnRule.java rename to src/main/java/org/betterx/bclib/interfaces/SpawnRule.java index 390efbf0..544ce1b5 100644 --- a/src/main/java/ru/bclib/interfaces/SpawnRule.java +++ b/src/main/java/org/betterx/bclib/interfaces/SpawnRule.java @@ -1,14 +1,17 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.level.LevelAccessor; -import java.util.Random;import net.minecraft.util.RandomSource; - @FunctionalInterface public interface SpawnRule { - boolean canSpawn(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random); + boolean canSpawn(EntityType type, + LevelAccessor world, + MobSpawnType spawnReason, + BlockPos pos, + RandomSource random); } diff --git a/src/main/java/ru/bclib/interfaces/SurfaceMaterialProvider.java b/src/main/java/org/betterx/bclib/interfaces/SurfaceMaterialProvider.java similarity index 74% rename from src/main/java/ru/bclib/interfaces/SurfaceMaterialProvider.java rename to src/main/java/org/betterx/bclib/interfaces/SurfaceMaterialProvider.java index 8dafa9a8..68fbe04f 100644 --- a/src/main/java/ru/bclib/interfaces/SurfaceMaterialProvider.java +++ b/src/main/java/org/betterx/bclib/interfaces/SurfaceMaterialProvider.java @@ -1,7 +1,8 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.api.surface.SurfaceRuleBuilder; + +import org.betterx.bclib.api.surface.SurfaceRuleBuilder; public interface SurfaceMaterialProvider { BlockState getTopMaterial(); diff --git a/src/main/java/ru/bclib/interfaces/SurfaceProvider.java b/src/main/java/org/betterx/bclib/interfaces/SurfaceProvider.java similarity index 67% rename from src/main/java/ru/bclib/interfaces/SurfaceProvider.java rename to src/main/java/org/betterx/bclib/interfaces/SurfaceProvider.java index 26426edf..60283118 100644 --- a/src/main/java/ru/bclib/interfaces/SurfaceProvider.java +++ b/src/main/java/org/betterx/bclib/interfaces/SurfaceProvider.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -7,5 +7,5 @@ import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.state.BlockState; public interface SurfaceProvider { - public BlockState bclib_getSurface(BlockPos pos, Holder biome, ServerLevel level); + BlockState bclib_getSurface(BlockPos pos, Holder biome, ServerLevel level); } diff --git a/src/main/java/org/betterx/bclib/interfaces/SurfaceRuleProvider.java b/src/main/java/org/betterx/bclib/interfaces/SurfaceRuleProvider.java new file mode 100644 index 00000000..acebef04 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/SurfaceRuleProvider.java @@ -0,0 +1,8 @@ +package org.betterx.bclib.interfaces; + +import net.minecraft.world.level.biome.BiomeSource; + +public interface SurfaceRuleProvider { + void bclib_addBiomeSource(BiomeSource source); + void bclib_clearBiomeSources(); +} diff --git a/src/main/java/ru/bclib/interfaces/SurvivesOnBlocks.java b/src/main/java/org/betterx/bclib/interfaces/SurvivesOnBlocks.java similarity index 81% rename from src/main/java/ru/bclib/interfaces/SurvivesOnBlocks.java rename to src/main/java/org/betterx/bclib/interfaces/SurvivesOnBlocks.java index 148606c6..f11fbb60 100644 --- a/src/main/java/ru/bclib/interfaces/SurvivesOnBlocks.java +++ b/src/main/java/org/betterx/bclib/interfaces/SurvivesOnBlocks.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; @@ -9,14 +9,14 @@ import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -public interface SurvivesOnBlocks extends SurvivesOnSpecialGround{ +public interface SurvivesOnBlocks extends SurvivesOnSpecialGround { List getSurvivableBlocks(); @Override - default String getSurvivableBlocksString(){ + default String getSurvivableBlocksString() { return getSurvivableBlocks() .stream() - .filter(block -> block!= Blocks.AIR && block!=null) + .filter(block -> block != Blocks.AIR && block != null) .map(block -> { ItemStack stack = new ItemStack(block); if (stack.hasCustomHoverName()) return stack.getHoverName().getString(); @@ -27,7 +27,7 @@ public interface SurvivesOnBlocks extends SurvivesOnSpecialGround{ } @Override - default boolean isSurvivable(BlockState state){ + default boolean isSurvivable(BlockState state) { return getSurvivableBlocks().contains(state.getBlock()); } } diff --git a/src/main/java/ru/bclib/interfaces/SurvivesOnSpecialGround.java b/src/main/java/org/betterx/bclib/interfaces/SurvivesOnSpecialGround.java similarity index 74% rename from src/main/java/ru/bclib/interfaces/SurvivesOnSpecialGround.java rename to src/main/java/org/betterx/bclib/interfaces/SurvivesOnSpecialGround.java index 3323a0b2..2a52cdc9 100644 --- a/src/main/java/ru/bclib/interfaces/SurvivesOnSpecialGround.java +++ b/src/main/java/org/betterx/bclib/interfaces/SurvivesOnSpecialGround.java @@ -1,31 +1,31 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; -import com.google.common.collect.Lists; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.Component; - import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.state.BlockState; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.common.collect.Lists; + import java.util.List; public interface SurvivesOnSpecialGround { String getSurvivableBlocksString(); @Environment(EnvType.CLIENT) - static List splitLines(String input) { + static List splitLines(String input) { final int MAX_LEN = 45; List lines = Lists.newArrayList(); - while (input.length()>MAX_LEN){ + while (input.length() > MAX_LEN) { int idx = input.lastIndexOf(",", MAX_LEN); - if (idx>=0) { - lines.add( input.substring(0, idx+1).trim()); - input = input.substring(idx+1).trim(); + if (idx >= 0) { + lines.add(input.substring(0, idx + 1).trim()); + input = input.substring(idx + 1).trim(); } else { break; } @@ -38,13 +38,13 @@ public interface SurvivesOnSpecialGround { @Environment(EnvType.CLIENT) static void appendHoverText(List list, String description) { final int MAX_LINES = 7; - List lines = splitLines(description); - if (lines.size()==1) { + List lines = splitLines(description); + if (lines.size() == 1) { list.add(Component.translatable("tooltip.bclib.place_on", lines.get(0)).withStyle(ChatFormatting.GREEN)); - } else if (lines.size()>1) { + } else if (lines.size() > 1) { list.add(Component.translatable("tooltip.bclib.place_on", "").withStyle(ChatFormatting.GREEN)); for (int i = 0; i < Math.min(lines.size(), MAX_LINES); i++) { - String line = lines.get(i); + String line = lines.get(i); if (i == MAX_LINES - 1 && i < lines.size() - 1) line += " ..."; list.add(Component.literal(" " + line).withStyle(ChatFormatting.GREEN)); } @@ -53,7 +53,7 @@ public interface SurvivesOnSpecialGround { boolean isSurvivable(BlockState state); - default boolean canSurviveOnTop(BlockState state, LevelReader world, BlockPos pos) { + default boolean canSurviveOnTop(BlockState state, LevelReader world, BlockPos pos) { return isSurvivable(world.getBlockState(pos.below())); } diff --git a/src/main/java/ru/bclib/interfaces/SurvivesOnTags.java b/src/main/java/org/betterx/bclib/interfaces/SurvivesOnTags.java similarity index 70% rename from src/main/java/ru/bclib/interfaces/SurvivesOnTags.java rename to src/main/java/org/betterx/bclib/interfaces/SurvivesOnTags.java index f4bee6b5..11820474 100644 --- a/src/main/java/ru/bclib/interfaces/SurvivesOnTags.java +++ b/src/main/java/org/betterx/bclib/interfaces/SurvivesOnTags.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.core.Registry; import net.minecraft.tags.TagKey; @@ -11,17 +11,17 @@ import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -public interface SurvivesOnTags extends SurvivesOnSpecialGround{ +public interface SurvivesOnTags extends SurvivesOnSpecialGround { List> getSurvivableTags(); @Override - default String getSurvivableBlocksString(){ + default String getSurvivableBlocksString() { return getSurvivableTags() .stream() .map(tag -> Registry.BLOCK.getTag(tag)) - .filter(named->named.isPresent()) - .map(named->named.get()) - .flatMap(named->named.stream()) + .filter(named -> named.isPresent()) + .map(named -> named.get()) + .flatMap(named -> named.stream()) .filter(block -> block != Blocks.AIR && block != null) .map(block -> { ItemStack stack = new ItemStack(block.value()); @@ -33,7 +33,7 @@ public interface SurvivesOnTags extends SurvivesOnSpecialGround{ } @Override - default boolean isSurvivable(BlockState state){ - return getSurvivableTags().stream().anyMatch(tag->state.is(tag)); + default boolean isSurvivable(BlockState state) { + return getSurvivableTags().stream().anyMatch(tag -> state.is(tag)); } } diff --git a/src/main/java/ru/bclib/interfaces/TagProvider.java b/src/main/java/org/betterx/bclib/interfaces/TagProvider.java similarity index 60% rename from src/main/java/ru/bclib/interfaces/TagProvider.java rename to src/main/java/org/betterx/bclib/interfaces/TagProvider.java index 7072308d..ee754cbd 100644 --- a/src/main/java/ru/bclib/interfaces/TagProvider.java +++ b/src/main/java/org/betterx/bclib/interfaces/TagProvider.java @@ -1,12 +1,11 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; - import java.util.List; public interface TagProvider { - void addTags(List> blockTags, List> itemTags); + void addTags(List> blockTags, List> itemTags); } diff --git a/src/main/java/ru/bclib/interfaces/TileEntityRenderProvider.java b/src/main/java/org/betterx/bclib/interfaces/TileEntityRenderProvider.java similarity index 55% rename from src/main/java/ru/bclib/interfaces/TileEntityRenderProvider.java rename to src/main/java/org/betterx/bclib/interfaces/TileEntityRenderProvider.java index 5d59f113..732d8f4b 100644 --- a/src/main/java/ru/bclib/interfaces/TileEntityRenderProvider.java +++ b/src/main/java/org/betterx/bclib/interfaces/TileEntityRenderProvider.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; public interface TileEntityRenderProvider { diff --git a/src/main/java/org/betterx/bclib/interfaces/TriConsumer.java b/src/main/java/org/betterx/bclib/interfaces/TriConsumer.java new file mode 100644 index 00000000..a06dcdcd --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/TriConsumer.java @@ -0,0 +1,6 @@ +package org.betterx.bclib.interfaces; + +@FunctionalInterface +public interface TriConsumer { + void accept(A a, B b, C c); +} diff --git a/src/main/java/ru/bclib/interfaces/UnknownReceipBookCategory.java b/src/main/java/org/betterx/bclib/interfaces/UnknownReceipBookCategory.java similarity index 55% rename from src/main/java/ru/bclib/interfaces/UnknownReceipBookCategory.java rename to src/main/java/org/betterx/bclib/interfaces/UnknownReceipBookCategory.java index e4a9300a..46052df4 100644 --- a/src/main/java/ru/bclib/interfaces/UnknownReceipBookCategory.java +++ b/src/main/java/org/betterx/bclib/interfaces/UnknownReceipBookCategory.java @@ -1,4 +1,4 @@ -package ru.bclib.interfaces; +package org.betterx.bclib.interfaces; public interface UnknownReceipBookCategory { } diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableAxe.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableAxe.java new file mode 100644 index 00000000..7a110d3d --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableAxe.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineableAxe { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableHammer.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableHammer.java new file mode 100644 index 00000000..6826e308 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableHammer.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineableHammer { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableHoe.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableHoe.java new file mode 100644 index 00000000..6db28739 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableHoe.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineableHoe { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineablePickaxe.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineablePickaxe.java new file mode 100644 index 00000000..9a9017a9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineablePickaxe.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineablePickaxe { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableShears.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableShears.java new file mode 100644 index 00000000..d26fba7b --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableShears.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineableShears { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableShovel.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableShovel.java new file mode 100644 index 00000000..d9ae344d --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableShovel.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineableShovel { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableSword.java b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableSword.java new file mode 100644 index 00000000..c38f3767 --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/AddMineableSword.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface AddMineableSword { +} diff --git a/src/main/java/org/betterx/bclib/interfaces/tools/PreventMineableAdd.java b/src/main/java/org/betterx/bclib/interfaces/tools/PreventMineableAdd.java new file mode 100644 index 00000000..5c75262f --- /dev/null +++ b/src/main/java/org/betterx/bclib/interfaces/tools/PreventMineableAdd.java @@ -0,0 +1,4 @@ +package org.betterx.bclib.interfaces.tools; + +public interface PreventMineableAdd { +} diff --git a/src/main/java/org/betterx/bclib/items/BaseAnvilItem.java b/src/main/java/org/betterx/bclib/items/BaseAnvilItem.java new file mode 100644 index 00000000..433cf2f7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/BaseAnvilItem.java @@ -0,0 +1,77 @@ +package org.betterx.bclib.items; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.core.Registry; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.IntegerProperty; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.blocks.BaseAnvilBlock; +import org.betterx.bclib.interfaces.BlockModelProvider; +import org.betterx.bclib.interfaces.ItemModelProvider; + +import java.util.List; +import java.util.Locale; +import org.jetbrains.annotations.Nullable; + +public class BaseAnvilItem extends BlockItem implements ItemModelProvider { + public final static String DESTRUCTION = "destruction"; + + public BaseAnvilItem(Block block, Properties properties) { + super(block, properties); + } + + @Override + protected BlockState getPlacementState(BlockPlaceContext blockPlaceContext) { + BlockState blockState = super.getPlacementState(blockPlaceContext); + ItemStack stack = blockPlaceContext.getItemInHand(); + int destruction = stack.getOrCreateTag().getInt(DESTRUCTION); + if (blockState != null) { + BaseAnvilBlock block = (BaseAnvilBlock) blockState.getBlock(); + IntegerProperty durabilityProp = block.getDurabilityProp(); + if (destruction == 0) { + blockState = blockState.setValue(durabilityProp, 0).setValue(BaseAnvilBlock.DESTRUCTION, 0); + } else { + int destructionValue = destruction / block.getMaxDurability(); + int durabilityValue = destruction - destructionValue * block.getMaxDurability(); + blockState = blockState.setValue(durabilityProp, durabilityValue) + .setValue(BaseAnvilBlock.DESTRUCTION, destructionValue); + } + } + return blockState; + } + + @Override + @Environment(EnvType.CLIENT) + public void appendHoverText(ItemStack itemStack, + @Nullable Level level, + List list, + TooltipFlag tooltipFlag) { + int destruction = itemStack.getOrCreateTag().getInt(DESTRUCTION); + if (destruction > 0) { + BaseAnvilBlock block = (BaseAnvilBlock) ((BaseAnvilItem) itemStack.getItem()).getBlock(); + int maxValue = block.getMaxDurability() * 3; + float damage = maxValue - destruction; + String percents = String.format(Locale.ROOT, "%.0f%%", damage); + list.add(Component.translatable("message.bclib.anvil_damage").append(": " + percents)); + } + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + Block anvilBlock = getBlock(); + ResourceLocation blockId = Registry.BLOCK.getKey(anvilBlock); + return ((BlockModelProvider) anvilBlock).getBlockModel(blockId, anvilBlock.defaultBlockState()); + } +} diff --git a/src/main/java/org/betterx/bclib/items/BaseArmorItem.java b/src/main/java/org/betterx/bclib/items/BaseArmorItem.java new file mode 100644 index 00000000..bc6e9206 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/BaseArmorItem.java @@ -0,0 +1,62 @@ +package org.betterx.bclib.items; + +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.ArmorMaterial; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import org.betterx.bclib.interfaces.ItemModelProvider; + +import java.util.UUID; + +public class BaseArmorItem extends ArmorItem implements ItemModelProvider { + + protected static final UUID[] ARMOR_MODIFIER_UUID_PER_SLOT = new UUID[]{ + UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), + UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), + UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), + UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150") + }; + + protected final Multimap defaultModifiers; + + public BaseArmorItem(ArmorMaterial material, EquipmentSlot equipmentSlot, Properties settings) { + super(material, equipmentSlot, settings); + this.defaultModifiers = HashMultimap.create(); + UUID uuid = ARMOR_MODIFIER_UUID_PER_SLOT[equipmentSlot.getIndex()]; + addAttributeModifier( + Attributes.ARMOR, + new AttributeModifier(uuid, "Armor modifier", getDefense(), AttributeModifier.Operation.ADDITION) + ); + addAttributeModifier( + Attributes.ARMOR_TOUGHNESS, + new AttributeModifier(uuid, "Armor toughness", getToughness(), AttributeModifier.Operation.ADDITION) + ); + if (knockbackResistance > 0.0F) { + addAttributeModifier( + Attributes.KNOCKBACK_RESISTANCE, + new AttributeModifier(uuid, + "Armor knockback resistance", + knockbackResistance, + AttributeModifier.Operation.ADDITION + ) + ); + } + } + + @Override + public Multimap getDefaultAttributeModifiers(EquipmentSlot equipmentSlot) { + return equipmentSlot == slot ? defaultModifiers : super.getDefaultAttributeModifiers(equipmentSlot); + } + + protected void addAttributeModifier(Attribute attribute, AttributeModifier modifier) { + if (defaultModifiers.containsKey(attribute)) { + defaultModifiers.removeAll(attribute); + } + defaultModifiers.put(attribute, modifier); + } +} diff --git a/src/main/java/org/betterx/bclib/items/BaseAttribute.java b/src/main/java/org/betterx/bclib/items/BaseAttribute.java new file mode 100644 index 00000000..2ff7cd72 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/BaseAttribute.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.items; + +import net.minecraft.world.entity.ai.attributes.Attribute; + +public class BaseAttribute extends Attribute { + public BaseAttribute(String description, double value) { + super(description, value); + } +} diff --git a/src/main/java/ru/bclib/items/BaseBucketItem.java b/src/main/java/org/betterx/bclib/items/BaseBucketItem.java similarity index 55% rename from src/main/java/ru/bclib/items/BaseBucketItem.java rename to src/main/java/org/betterx/bclib/items/BaseBucketItem.java index 9a4e8789..dc783708 100644 --- a/src/main/java/ru/bclib/items/BaseBucketItem.java +++ b/src/main/java/org/betterx/bclib/items/BaseBucketItem.java @@ -1,14 +1,16 @@ -package ru.bclib.items; +package org.betterx.bclib.items; -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.MobBucketItem; import net.minecraft.world.level.material.Fluids; -import ru.bclib.interfaces.ItemModelProvider; + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; + +import org.betterx.bclib.interfaces.ItemModelProvider; public class BaseBucketItem extends MobBucketItem implements ItemModelProvider { - public BaseBucketItem(EntityType type, FabricItemSettings settings) { - super(type, Fluids.WATER, SoundEvents.BUCKET_EMPTY_FISH, settings.stacksTo(1)); - } + public BaseBucketItem(EntityType type, FabricItemSettings settings) { + super(type, Fluids.WATER, SoundEvents.BUCKET_EMPTY_FISH, settings.stacksTo(1)); + } } diff --git a/src/main/java/org/betterx/bclib/items/BaseDiscItem.java b/src/main/java/org/betterx/bclib/items/BaseDiscItem.java new file mode 100644 index 00000000..f4a4d96d --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/BaseDiscItem.java @@ -0,0 +1,12 @@ +package org.betterx.bclib.items; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.RecordItem; + +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class BaseDiscItem extends RecordItem implements ItemModelProvider { + public BaseDiscItem(int comparatorOutput, SoundEvent sound, Properties settings) { + super(comparatorOutput, sound, settings); + } +} diff --git a/src/main/java/org/betterx/bclib/items/BaseDrinkItem.java b/src/main/java/org/betterx/bclib/items/BaseDrinkItem.java new file mode 100644 index 00000000..85b3ebeb --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/BaseDrinkItem.java @@ -0,0 +1,59 @@ +package org.betterx.bclib.items; + +import net.minecraft.advancements.CriteriaTriggers; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.Stats; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemUtils; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.UseAnim; +import net.minecraft.world.level.Level; + +public class BaseDrinkItem extends ModelProviderItem { + public BaseDrinkItem(Properties settings) { + super(settings); + } + + @Override + public int getUseDuration(ItemStack stack) { + return 32; + } + + @Override + public UseAnim getUseAnimation(ItemStack stack) { + return UseAnim.DRINK; + } + + @Override + public InteractionResultHolder use(Level world, Player user, InteractionHand hand) { + return ItemUtils.startUsingInstantly(world, user, hand); + } + + @Override + public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity user) { + if (this.isEdible()) { + int count = stack.getCount(); + user.eat(level, stack); + stack.setCount(count); + } + + if (user instanceof ServerPlayer serverPlayerEntity) { + CriteriaTriggers.CONSUME_ITEM.trigger(serverPlayerEntity, stack); + serverPlayerEntity.awardStat(Stats.ITEM_USED.get(this)); + } + + if (user instanceof Player && !((Player) user).getAbilities().instabuild) { + stack.shrink(1); + } + + if (!level.isClientSide) { + user.removeAllEffects(); + } + + return stack.isEmpty() ? new ItemStack(Items.GLASS_BOTTLE) : stack; + } +} diff --git a/src/main/java/org/betterx/bclib/items/BaseSpawnEggItem.java b/src/main/java/org/betterx/bclib/items/BaseSpawnEggItem.java new file mode 100644 index 00000000..29ac4b60 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/BaseSpawnEggItem.java @@ -0,0 +1,30 @@ +package org.betterx.bclib.items; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.item.SpawnEggItem; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.BasePatterns; +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.client.models.PatternsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +import java.util.Optional; + +public class BaseSpawnEggItem extends SpawnEggItem implements ItemModelProvider { + public BaseSpawnEggItem(EntityType type, int primaryColor, int secondaryColor, Properties settings) { + super(type, primaryColor, secondaryColor, settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_SPAWN_EGG, resourceLocation); + return ModelsHelper.fromPattern(pattern); + } +} diff --git a/src/main/java/org/betterx/bclib/items/ModelProviderItem.java b/src/main/java/org/betterx/bclib/items/ModelProviderItem.java new file mode 100644 index 00000000..f082966e --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/ModelProviderItem.java @@ -0,0 +1,23 @@ +package org.betterx.bclib.items; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class ModelProviderItem extends Item implements ItemModelProvider { + public ModelProviderItem(Properties settings) { + super(settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createItemModel(resourceLocation); + } +} diff --git a/src/main/java/org/betterx/bclib/items/tool/BaseAxeItem.java b/src/main/java/org/betterx/bclib/items/tool/BaseAxeItem.java new file mode 100644 index 00000000..1d9a1e66 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/tool/BaseAxeItem.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.items.tool; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.AxeItem; +import net.minecraft.world.item.Tier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class BaseAxeItem extends AxeItem implements ItemModelProvider { + public BaseAxeItem(Tier material, float attackDamage, float attackSpeed, Properties settings) { + super(material, attackDamage, attackSpeed, settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createHandheldItem(resourceLocation); + } +} diff --git a/src/main/java/org/betterx/bclib/items/tool/BaseHoeItem.java b/src/main/java/org/betterx/bclib/items/tool/BaseHoeItem.java new file mode 100644 index 00000000..d8d7cbdb --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/tool/BaseHoeItem.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.items.tool; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.HoeItem; +import net.minecraft.world.item.Tier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class BaseHoeItem extends HoeItem implements ItemModelProvider { + public BaseHoeItem(Tier material, int attackDamage, float attackSpeed, Properties settings) { + super(material, attackDamage, attackSpeed, settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createHandheldItem(resourceLocation); + } +} diff --git a/src/main/java/org/betterx/bclib/items/tool/BasePickaxeItem.java b/src/main/java/org/betterx/bclib/items/tool/BasePickaxeItem.java new file mode 100644 index 00000000..349e9f3f --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/tool/BasePickaxeItem.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.items.tool; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.PickaxeItem; +import net.minecraft.world.item.Tier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class BasePickaxeItem extends PickaxeItem implements ItemModelProvider { + public BasePickaxeItem(Tier material, int attackDamage, float attackSpeed, Properties settings) { + super(material, attackDamage, attackSpeed, settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createHandheldItem(resourceLocation); + } +} diff --git a/src/main/java/org/betterx/bclib/items/tool/BaseShearsItem.java b/src/main/java/org/betterx/bclib/items/tool/BaseShearsItem.java new file mode 100644 index 00000000..384223b2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/tool/BaseShearsItem.java @@ -0,0 +1,31 @@ +package org.betterx.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 net.fabricmc.fabric.api.mininglevel.v1.FabricMineableTags; + +import org.betterx.bclib.api.tag.CommonItemTags; +import org.betterx.bclib.api.tag.TagAPI; + +public class BaseShearsItem extends ShearsItem { + public BaseShearsItem(Properties properties) { + super(properties); + } + + public static boolean isShear(ItemStack tool) { + return tool.is(Items.SHEARS) | tool.is(CommonItemTags.SHEARS) || TagAPI.isToolWithMineableTag(tool, + FabricMineableTags.SHEARS_MINEABLE); + } + + public static boolean isShear(ItemStack itemStack, Item item) { + if (item == Items.SHEARS) { + return itemStack.is(item) | itemStack.is(CommonItemTags.SHEARS); + } else { + return itemStack.is(item); + } + } +} diff --git a/src/main/java/org/betterx/bclib/items/tool/BaseShovelItem.java b/src/main/java/org/betterx/bclib/items/tool/BaseShovelItem.java new file mode 100644 index 00000000..2135b5ed --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/tool/BaseShovelItem.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.items.tool; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ShovelItem; +import net.minecraft.world.item.Tier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class BaseShovelItem extends ShovelItem implements ItemModelProvider { + public BaseShovelItem(Tier material, float attackDamage, float attackSpeed, Properties settings) { + super(material, attackDamage, attackSpeed, settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createHandheldItem(resourceLocation); + } +} diff --git a/src/main/java/org/betterx/bclib/items/tool/BaseSwordItem.java b/src/main/java/org/betterx/bclib/items/tool/BaseSwordItem.java new file mode 100644 index 00000000..bcfec7d2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/items/tool/BaseSwordItem.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.items.tool; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.SwordItem; +import net.minecraft.world.item.Tier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.betterx.bclib.client.models.ModelsHelper; +import org.betterx.bclib.interfaces.ItemModelProvider; + +public class BaseSwordItem extends SwordItem implements ItemModelProvider { + public BaseSwordItem(Tier material, int attackDamage, float attackSpeed, Properties settings) { + super(material, attackDamage, attackSpeed, settings); + } + + @Override + @Environment(EnvType.CLIENT) + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createHandheldItem(resourceLocation); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/client/AnvilScreenMixin.java b/src/main/java/org/betterx/bclib/mixin/client/AnvilScreenMixin.java new file mode 100644 index 00000000..56039a58 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/AnvilScreenMixin.java @@ -0,0 +1,93 @@ +package org.betterx.bclib.mixin.client; + +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.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 com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import org.betterx.bclib.interfaces.AnvilScreenHandlerExtended; +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 java.util.List; + +@Mixin(AnvilScreen.class) +public class AnvilScreenMixin extends ItemCombinerScreen { + + @Shadow + private EditBox name; + + private final List 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, Component.literal("<"), b -> be_previousRecipe())); + be_buttons.add(new Button(x + 154, y + 45, 15, 20, Component.literal(">"), 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); + } +} diff --git a/src/main/java/ru/bclib/mixin/client/BlockMixin.java b/src/main/java/org/betterx/bclib/mixin/client/BlockMixin.java similarity index 56% rename from src/main/java/ru/bclib/mixin/client/BlockMixin.java rename to src/main/java/org/betterx/bclib/mixin/client/BlockMixin.java index d4f92437..c2beac57 100644 --- a/src/main/java/ru/bclib/mixin/client/BlockMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/client/BlockMixin.java @@ -1,24 +1,29 @@ -package ru.bclib.mixin.client; +package org.betterx.bclib.mixin.client; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.Block; -import org.jetbrains.annotations.Nullable; + +import org.betterx.bclib.interfaces.SurvivesOnSpecialGround; 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; -import ru.bclib.interfaces.SurvivesOnSpecialGround; import java.util.List; +import org.jetbrains.annotations.Nullable; @Mixin(Block.class) public class BlockMixin { - @Inject(method="appendHoverText", at=@At("HEAD")) - void bclib_appendSurvivalBlock(ItemStack itemStack, @Nullable BlockGetter blockGetter, List list, TooltipFlag tooltipFlag, CallbackInfo ci){ - if (this instanceof SurvivesOnSpecialGround surv){ + @Inject(method = "appendHoverText", at = @At("HEAD")) + void bclib_appendSurvivalBlock(ItemStack itemStack, + @Nullable BlockGetter blockGetter, + List list, + TooltipFlag tooltipFlag, + CallbackInfo ci) { + if (this instanceof SurvivesOnSpecialGround surv) { SurvivesOnSpecialGround.appendHoverText(list, surv.getSurvivableBlocksString()); } } diff --git a/src/main/java/ru/bclib/mixin/client/ClientRecipeBookMixin.java b/src/main/java/org/betterx/bclib/mixin/client/ClientRecipeBookMixin.java similarity index 50% rename from src/main/java/ru/bclib/mixin/client/ClientRecipeBookMixin.java rename to src/main/java/org/betterx/bclib/mixin/client/ClientRecipeBookMixin.java index 329573e3..040ea733 100644 --- a/src/main/java/ru/bclib/mixin/client/ClientRecipeBookMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/client/ClientRecipeBookMixin.java @@ -1,20 +1,21 @@ -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 info) { - if (recipe instanceof UnknownReceipBookCategory) { - info.setReturnValue(RecipeBookCategories.UNKNOWN); - } - } -} +package org.betterx.bclib.mixin.client; + +import net.minecraft.client.ClientRecipeBook; +import net.minecraft.client.RecipeBookCategories; +import net.minecraft.world.item.crafting.Recipe; + +import org.betterx.bclib.interfaces.UnknownReceipBookCategory; +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; + +@Mixin(ClientRecipeBook.class) +public abstract class ClientRecipeBookMixin { + @Inject(method = "getCategory", at = @At("HEAD"), cancellable = true) + private static void be_getGroupForRecipe(Recipe recipe, CallbackInfoReturnable info) { + if (recipe instanceof UnknownReceipBookCategory) { + info.setReturnValue(RecipeBookCategories.UNKNOWN); + } + } +} diff --git a/src/main/java/ru/bclib/mixin/client/CreateWorldScreenMixin.java b/src/main/java/org/betterx/bclib/mixin/client/CreateWorldScreenMixin.java similarity index 54% rename from src/main/java/ru/bclib/mixin/client/CreateWorldScreenMixin.java rename to src/main/java/org/betterx/bclib/mixin/client/CreateWorldScreenMixin.java index ee3708c5..53eb45b8 100644 --- a/src/main/java/ru/bclib/mixin/client/CreateWorldScreenMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/client/CreateWorldScreenMixin.java @@ -1,4 +1,4 @@ -package ru.bclib.mixin.client; +package org.betterx.bclib.mixin.client; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; @@ -6,19 +6,19 @@ import net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent import net.minecraft.core.Registry; import net.minecraft.world.level.DataPackConfig; +import org.betterx.bclib.api.biomes.BiomeAPI; 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; -import ru.bclib.api.biomes.BiomeAPI; @Mixin(CreateWorldScreen.class) public class CreateWorldScreenMixin { - @Inject(method = "", at = @At("TAIL")) - private void bcl_init(Screen screen, - DataPackConfig dataPackConfig, - WorldGenSettingsComponent worldGenSettingsComponent, - CallbackInfo ci) { - BiomeAPI.initRegistry(worldGenSettingsComponent.registryHolder().registryOrThrow(Registry.BIOME_REGISTRY)); - } + @Inject(method = "", at = @At("TAIL")) + private void bcl_init(Screen screen, + DataPackConfig dataPackConfig, + WorldGenSettingsComponent worldGenSettingsComponent, + CallbackInfo ci) { + BiomeAPI.initRegistry(worldGenSettingsComponent.registryHolder().registryOrThrow(Registry.BIOME_REGISTRY)); + } } diff --git a/src/main/java/org/betterx/bclib/mixin/client/FogRendererMixin.java b/src/main/java/org/betterx/bclib/mixin/client/FogRendererMixin.java new file mode 100644 index 00000000..accd9bd2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/FogRendererMixin.java @@ -0,0 +1,68 @@ +package org.betterx.bclib.mixin.client; + +import net.minecraft.client.Camera; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.FogType; + +import org.betterx.bclib.client.render.CustomFogRenderer; +import org.betterx.bclib.util.BackgroundInfo; +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; + +@Mixin(FogRenderer.class) +public class FogRendererMixin { + @Shadow + private static float fogRed; + @Shadow + private static float fogGreen; + @Shadow + private static float fogBlue; + + @Inject(method = "setupColor", at = @At("RETURN")) + private static void bclib_onRender(Camera camera, + float tickDelta, + ClientLevel world, + int i, + float f, + CallbackInfo info) { + FogType fogType = camera.getFluidInCamera(); + if (fogType != FogType.WATER && world.dimension().equals(Level.END)) { + Entity entity = camera.getEntity(); + boolean skip = false; + if (entity instanceof LivingEntity) { + MobEffectInstance effect = ((LivingEntity) entity).getEffect(MobEffects.NIGHT_VISION); + skip = effect != null && effect.getDuration() > 0; + } + if (!skip) { + fogRed *= 4; + fogGreen *= 4; + fogBlue *= 4; + } + } + + BackgroundInfo.fogColorRed = fogRed; + BackgroundInfo.fogColorGreen = fogGreen; + BackgroundInfo.fogColorBlue = fogBlue; + } + + @Inject(method = "setupFog", at = @At("HEAD"), cancellable = true) + private static void bclib_fogDensity(Camera camera, + FogRenderer.FogMode fogMode, + float viewDistance, + boolean thickFog, + float g, + CallbackInfo ci) { + if (CustomFogRenderer.applyFogDensity(camera, viewDistance, thickFog)) { + ci.cancel(); + } + } +} diff --git a/src/main/java/ru/bclib/mixin/client/GameMixin.java b/src/main/java/org/betterx/bclib/mixin/client/GameMixin.java similarity index 53% rename from src/main/java/ru/bclib/mixin/client/GameMixin.java rename to src/main/java/org/betterx/bclib/mixin/client/GameMixin.java index 73c16bf0..d9b73c6b 100644 --- a/src/main/java/ru/bclib/mixin/client/GameMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/client/GameMixin.java @@ -1,17 +1,18 @@ -package ru.bclib.mixin.client; +package org.betterx.bclib.mixin.client; import net.minecraft.client.Game; + +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; 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; -import ru.bclib.api.dataexchange.DataExchangeAPI; @Mixin(Game.class) public class GameMixin { - - @Inject(method="onStartGameSession", at=@At("TAIL")) - public void bclib_onStart(CallbackInfo ci){ - DataExchangeAPI.sendOnEnter(); - } + + @Inject(method = "onStartGameSession", at = @At("TAIL")) + public void bclib_onStart(CallbackInfo ci) { + DataExchangeAPI.sendOnEnter(); + } } diff --git a/src/main/java/org/betterx/bclib/mixin/client/MinecraftMixin.java b/src/main/java/org/betterx/bclib/mixin/client/MinecraftMixin.java new file mode 100644 index 00000000..65abc592 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/MinecraftMixin.java @@ -0,0 +1,36 @@ +package org.betterx.bclib.mixin.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.color.item.ItemColors; +import net.minecraft.client.main.GameConfig; +import net.minecraft.core.Registry; + +import org.betterx.bclib.interfaces.CustomColorProvider; +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.CallbackInfo; + +@Mixin(Minecraft.class) +public abstract class MinecraftMixin { + @Final + @Shadow + private BlockColors blockColors; + + @Final + @Shadow + private ItemColors itemColors; + + @Inject(method = "*", at = @At("TAIL")) + private void bclib_onMCInit(GameConfig args, CallbackInfo info) { + Registry.BLOCK.forEach(block -> { + if (block instanceof CustomColorProvider provider) { + blockColors.register(provider.getProvider(), block); + itemColors.register(provider.getItemProvider(), block.asItem()); + } + }); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/client/ModelBakeryMixin.java b/src/main/java/org/betterx/bclib/mixin/client/ModelBakeryMixin.java new file mode 100644 index 00000000..5ca9afa2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/ModelBakeryMixin.java @@ -0,0 +1,38 @@ +package org.betterx.bclib.mixin.client; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.profiling.ProfilerFiller; + +import org.betterx.bclib.api.ModIntegrationAPI; +import org.betterx.bclib.client.models.CustomModelBakery; +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.CallbackInfo; + +import java.util.Map; + +@Mixin(ModelBakery.class) +public abstract class ModelBakeryMixin { + @Final + @Shadow + private Map unbakedCache; + + @Inject(method = "*", at = @At("TAIL")) + private void bclib_findEmissiveModels(ResourceManager resourceManager, + BlockColors blockColors, + ProfilerFiller profiler, + int mipmap, + CallbackInfo info) { + //CustomModelBakery.setModelsLoaded(false); + if (ModIntegrationAPI.hasCanvas()) { + CustomModelBakery.loadEmissiveModels(unbakedCache); + } + } +} diff --git a/src/main/java/ru/bclib/mixin/client/ModelManagerMixin.java b/src/main/java/org/betterx/bclib/mixin/client/ModelManagerMixin.java similarity index 54% rename from src/main/java/ru/bclib/mixin/client/ModelManagerMixin.java rename to src/main/java/org/betterx/bclib/mixin/client/ModelManagerMixin.java index c7b28d17..5ed59d8b 100644 --- a/src/main/java/ru/bclib/mixin/client/ModelManagerMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/client/ModelManagerMixin.java @@ -1,19 +1,22 @@ -package ru.bclib.mixin.client; +package org.betterx.bclib.mixin.client; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.util.profiling.ProfilerFiller; + +import org.betterx.bclib.client.BCLibClient; 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.client.BCLibClient; @Mixin(ModelManager.class) public class ModelManagerMixin { - @Inject(method = "prepare", at = @At("HEAD")) - private void bclib_loadCustomModels(ResourceManager resourceManager, ProfilerFiller profilerFiller, CallbackInfoReturnable info) { - BCLibClient.modelBakery.loadCustomModels(resourceManager); - } + @Inject(method = "prepare", at = @At("HEAD")) + private void bclib_loadCustomModels(ResourceManager resourceManager, + ProfilerFiller profilerFiller, + CallbackInfoReturnable info) { + BCLibClient.modelBakery.loadCustomModels(resourceManager); + } } diff --git a/src/main/java/org/betterx/bclib/mixin/client/MultiPackResourceManagerMixin.java b/src/main/java/org/betterx/bclib/mixin/client/MultiPackResourceManagerMixin.java new file mode 100644 index 00000000..b8bea188 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/MultiPackResourceManagerMixin.java @@ -0,0 +1,55 @@ +package org.betterx.bclib.mixin.client; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.FallbackResourceManager; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.ModIntegrationAPI; +import org.betterx.bclib.client.render.EmissiveTextureInfo; +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 java.io.IOException; +import java.util.Map; + +@Mixin(MultiPackResourceManager.class) +public class MultiPackResourceManagerMixin { + @Final + @Shadow + private Map namespacedManagers; + + private final ResourceLocation bclib_alphaEmissionMaterial = BCLib.makeID("materialmaps/block/alpha_emission.json"); + + @Inject(method = "getResource", at = @At("HEAD"), cancellable = true) + private void bclib_getResource(ResourceLocation resourceLocation, + CallbackInfoReturnable info) throws IOException { + if (!ModIntegrationAPI.hasCanvas()) { + return; + } + if (!resourceLocation.getPath().startsWith("materialmaps")) { + return; + } + if (!resourceLocation.getPath().contains("/block/")) { + return; + } + + String name = resourceLocation.getPath().replace("materialmaps/block/", "").replace(".json", ""); + ResourceLocation blockID = new ResourceLocation(resourceLocation.getNamespace(), name); + + if (!EmissiveTextureInfo.isEmissiveBlock(blockID)) { + return; + } + + ResourceManager resourceManager = this.namespacedManagers.get(resourceLocation.getNamespace()); + if (resourceManager != null && resourceManager.getResource(resourceLocation).isEmpty()) { + info.setReturnValue(resourceManager.getResource(bclib_alphaEmissionMaterial).get()); + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/client/SignEditScreenMixin.java b/src/main/java/org/betterx/bclib/mixin/client/SignEditScreenMixin.java new file mode 100644 index 00000000..245620bc --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/SignEditScreenMixin.java @@ -0,0 +1,77 @@ +package org.betterx.bclib.mixin.client; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.SignEditScreen; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.SignRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import org.betterx.bclib.blocks.BaseSignBlock; +import org.betterx.bclib.client.render.BaseSignBlockEntityRenderer; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(SignEditScreen.class) +public abstract class SignEditScreenMixin extends Screen { + @Shadow + @Final + private SignBlockEntity sign; + @Shadow + private SignRenderer.SignModel signModel; + @Unique + private boolean bclib_renderStick; + @Unique + private boolean bclib_isSign; + + protected SignEditScreenMixin(Component component) { + super(component); + } + + @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;IIF)V", at = @At( + value = "INVOKE", + target = "Lcom/mojang/blaze3d/vertex/PoseStack;pushPose()V", + shift = Shift.BEFORE + ), locals = LocalCapture.CAPTURE_FAILSOFT) + private void bclib_checkOffset(PoseStack poseStack, + int i, + int j, + float f, + CallbackInfo info, + float g, + BlockState blockState, + boolean bl, + boolean bl2, + float h) { + bclib_isSign = blockState.getBlock() instanceof BaseSignBlock; + if (bclib_isSign) { + bclib_renderStick = blockState.getValue(BaseSignBlock.FLOOR); + if (bclib_renderStick) { + poseStack.translate(0.0, 0.3125, 0.0); + } + } + } + + @ModifyArg(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;IIF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;II)V"), index = 1) + private VertexConsumer bclib_signRender(VertexConsumer consumer) { + if (bclib_isSign) { + signModel.stick.visible = bclib_renderStick; + Block block = sign.getBlockState().getBlock(); + MultiBufferSource.BufferSource bufferSource = this.minecraft.renderBuffers().bufferSource(); + return BaseSignBlockEntityRenderer.getConsumer(bufferSource, block); + } + return consumer; + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/client/TextureAtlasMixin.java b/src/main/java/org/betterx/bclib/mixin/client/TextureAtlasMixin.java new file mode 100644 index 00000000..9a7864b8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/client/TextureAtlasMixin.java @@ -0,0 +1,111 @@ +package org.betterx.bclib.mixin.client; + +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; + +import net.fabricmc.fabric.impl.client.texture.FabricSprite; +import net.fabricmc.loader.api.FabricLoader; + +import com.mojang.blaze3d.platform.NativeImage; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.client.render.EmissiveTextureInfo; +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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.IOException; +import java.util.Optional; + +@Mixin(TextureAtlas.class) +public class TextureAtlasMixin { + private static final int EMISSIVE_ALPHA = 254 << 24; + private boolean bclib_modifyAtlas; + + @Inject(method = "*", at = @At("TAIL")) + private void bclib_onAtlasInit(ResourceLocation resourceLocation, CallbackInfo info) { + boolean hasOptifine = FabricLoader.getInstance().isModLoaded("optifabric"); + bclib_modifyAtlas = !hasOptifine && resourceLocation.toString().equals("minecraft:textures/atlas/blocks.png"); + if (bclib_modifyAtlas) { + EmissiveTextureInfo.clear(); + } + } + + @Inject(method = "load(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIII)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;", at = @At("HEAD"), cancellable = true) + private void bclib_loadSprite(ResourceManager resourceManager, + TextureAtlasSprite.Info spriteInfo, + int atlasWidth, + int atlasHeight, + int maxLevel, + int posX, + int posY, + CallbackInfoReturnable info) { + if (!bclib_modifyAtlas) { + return; + } + + ResourceLocation location = spriteInfo.name(); + if (!location.getPath().startsWith("block")) { + return; + } + + ResourceLocation emissiveLocation = new ResourceLocation( + location.getNamespace(), + "textures/" + location.getPath() + "_e.png" + ); + Optional emissiveRes = resourceManager.getResource(emissiveLocation); + if (emissiveRes.isPresent()) { + NativeImage sprite = null; + NativeImage emission = null; + try { + ResourceLocation spriteLocation = new ResourceLocation( + location.getNamespace(), + "textures/" + location.getPath() + ".png" + ); + Resource resource = resourceManager.getResource(spriteLocation).orElse(null); + sprite = NativeImage.read(resource.open()); + + resource = emissiveRes.get(); + emission = NativeImage.read(resource.open()); + } catch (IOException e) { + BCLib.LOGGER.warning(e.getMessage()); + } + if (sprite != null && emission != null) { + int width = Math.min(sprite.getWidth(), emission.getWidth()); + int height = Math.min(sprite.getHeight(), emission.getHeight()); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int argb = emission.getPixelRGBA(x, y); + int alpha = (argb >> 24) & 255; + if (alpha > 127) { + int r = (argb >> 16) & 255; + int g = (argb >> 8) & 255; + int b = argb & 255; + if (r > 0 || g > 0 || b > 0) { + argb = (argb & 0x00FFFFFF) | EMISSIVE_ALPHA; + sprite.setPixelRGBA(x, y, argb); + } + } + } + } + TextureAtlas self = (TextureAtlas) (Object) this; + FabricSprite result = new FabricSprite( + self, + spriteInfo, + maxLevel, + atlasWidth, + atlasHeight, + posX, + posY, + sprite + ); + EmissiveTextureInfo.addTexture(location); + info.setReturnValue(result); + } + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/AnvilBlockMixin.java b/src/main/java/org/betterx/bclib/mixin/common/AnvilBlockMixin.java new file mode 100644 index 00000000..8752470b --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/AnvilBlockMixin.java @@ -0,0 +1,21 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.level.block.AnvilBlock; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.blocks.BaseAnvilBlock; +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; + +@Mixin(AnvilBlock.class) +public class AnvilBlockMixin { + @Inject(method = "damage", at = @At("HEAD"), cancellable = true) + private static void bclib_anvilDamage(BlockState state, CallbackInfoReturnable info) { + if (state.getBlock() instanceof BaseAnvilBlock) { + BaseAnvilBlock anvil = (BaseAnvilBlock) state.getBlock(); + info.setReturnValue(anvil.damageAnvilFall(state)); + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/AnvilMenuMixin.java b/src/main/java/org/betterx/bclib/mixin/common/AnvilMenuMixin.java new file mode 100644 index 00000000..060302e9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/AnvilMenuMixin.java @@ -0,0 +1,208 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.tags.BlockTags; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.*; +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.betterx.bclib.blocks.BaseAnvilBlock; +import org.betterx.bclib.blocks.LeveledAnvilBlock; +import org.betterx.bclib.interfaces.AnvilScreenHandlerExtended; +import org.betterx.bclib.recipes.AnvilRecipe; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.jetbrains.annotations.Nullable; + +@Mixin(AnvilMenu.class) +public abstract class AnvilMenuMixin extends ItemCombinerMenu implements AnvilScreenHandlerExtended { + private List be_recipes = Collections.emptyList(); + private AnvilRecipe be_currentRecipe; + private DataSlot anvilLevel; + + @Shadow + private int repairItemCountCost; + + @Final + @Shadow + private DataSlot cost; + + public AnvilMenuMixin(@Nullable MenuType menuType, + int i, + Inventory inventory, + ContainerLevelAccess containerLevelAccess) { + super(menuType, i, inventory, containerLevelAccess); + } + + @Inject(method = "(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 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) { + info.cancel(); + if (!player.getAbilities().instabuild) { + player.giveExperienceLevels(-this.cost.get()); + } + + this.inputSlots.setItem(0, ItemStack.EMPTY); + if (this.repairItemCountCost > 0) { + ItemStack itemStack2 = this.inputSlots.getItem(1); + if (!itemStack2.isEmpty() && itemStack2.getCount() > this.repairItemCountCost) { + itemStack2.shrink(this.repairItemCountCost); + this.inputSlots.setItem(1, itemStack2); + } else { + this.inputSlots.setItem(1, ItemStack.EMPTY); + } + } else { + this.inputSlots.setItem(1, ItemStack.EMPTY); + } + + this.cost.set(0); + + if (!player.getAbilities().instabuild && blockState.is(BlockTags.ANVIL) && player.getRandom() + .nextFloat() < 0.12F) { + BaseAnvilBlock anvil = (BaseAnvilBlock) blockState.getBlock(); + BlockState damaged = anvil.damageAnvilUse(blockState, player.getRandom()); + if (damaged == null) { + level.removeBlock(blockPos, false); + level.levelEvent(1029, blockPos, 0); + } else { + level.setBlock(blockPos, damaged, 2); + level.levelEvent(1030, blockPos, 0); + } + } else { + level.levelEvent(1030, blockPos, 0); + } + } + }); + } + + @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 be_getRecipes() { + return be_recipes; + } +} diff --git a/src/main/java/ru/bclib/mixin/common/BiomeGenerationSettingsAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/BiomeGenerationSettingsAccessor.java similarity index 50% rename from src/main/java/ru/bclib/mixin/common/BiomeGenerationSettingsAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/BiomeGenerationSettingsAccessor.java index 5060858c..15c80ed4 100644 --- a/src/main/java/ru/bclib/mixin/common/BiomeGenerationSettingsAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/BiomeGenerationSettingsAccessor.java @@ -1,4 +1,4 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.core.HolderSet; import net.minecraft.world.level.biome.BiomeGenerationSettings; @@ -6,6 +6,7 @@ import net.minecraft.world.level.levelgen.GenerationStep; import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.levelgen.placement.PlacedFeature; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @@ -17,22 +18,22 @@ import java.util.function.Supplier; @Mixin(BiomeGenerationSettings.class) public interface BiomeGenerationSettingsAccessor { - @Accessor("features") - List> bclib_getFeatures(); - - @Accessor("features") - @Mutable - void bclib_setFeatures(List> value); - - @Accessor("featureSet") - void bclib_setFeatureSet(Supplier> featureSet); + @Accessor("features") + List> bclib_getFeatures(); - @Accessor("flowerFeatures") - void bclib_setFlowerFeatures(Supplier>> flowerFeatures); - - @Accessor("carvers") - Map>> bclib_getCarvers(); - - @Accessor("carvers") - void bclib_setCarvers(Map>> features); + @Accessor("features") + @Mutable + void bclib_setFeatures(List> value); + + @Accessor("featureSet") + void bclib_setFeatureSet(Supplier> featureSet); + + @Accessor("flowerFeatures") + void bclib_setFlowerFeatures(Supplier>> flowerFeatures); + + @Accessor("carvers") + Map>> bclib_getCarvers(); + + @Accessor("carvers") + void bclib_setCarvers(Map>> features); } diff --git a/src/main/java/ru/bclib/mixin/common/BiomeMixin.java b/src/main/java/org/betterx/bclib/mixin/common/BiomeMixin.java similarity index 77% rename from src/main/java/ru/bclib/mixin/common/BiomeMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/BiomeMixin.java index 8f68b8cc..bcade6fd 100644 --- a/src/main/java/ru/bclib/mixin/common/BiomeMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/BiomeMixin.java @@ -1,4 +1,4 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.world.level.biome.Biome; diff --git a/src/main/java/org/betterx/bclib/mixin/common/BiomeSourceMixin.java b/src/main/java/org/betterx/bclib/mixin/common/BiomeSourceMixin.java new file mode 100644 index 00000000..334957a2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/BiomeSourceMixin.java @@ -0,0 +1,36 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.BiomeSource.StepFeatureData; + +import com.google.common.base.Suppliers; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.interfaces.BiomeSourceAccessor; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +@Mixin(BiomeSource.class) +public abstract class BiomeSourceMixin implements BiomeSourceAccessor { + @Shadow + protected abstract List buildFeaturesPerStep(List list, boolean bl); + + @Shadow + public abstract Set possibleBiomes(); + + @Mutable + @Shadow + @Final + private Supplier> featuresPerStep; + + public void bclRebuildFeatures() { + BCLib.LOGGER.info("Rebuilding features in BiomeSource " + this); + featuresPerStep = Suppliers.memoize(() -> buildFeaturesPerStep(this.possibleBiomes().stream().toList(), true)); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/BlockStateBaseMixin.java b/src/main/java/org/betterx/bclib/mixin/common/BlockStateBaseMixin.java new file mode 100644 index 00000000..adaf3796 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/BlockStateBaseMixin.java @@ -0,0 +1,23 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase; + +import org.betterx.bclib.util.MethodReplace; +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 java.util.function.Function; + +@Mixin(BlockStateBase.class) +public class BlockStateBaseMixin { + @Inject(method = "is(Lnet/minecraft/world/level/block/Block;)Z", at = @At("HEAD"), cancellable = true) + private void bclib_replaceFunction(Block block, CallbackInfoReturnable info) { + Function replacement = MethodReplace.getBlockReplace(block); + if (replacement != null) { + info.setReturnValue(replacement.apply(BlockStateBase.class.cast(this))); + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/BoneMealItemMixin.java b/src/main/java/org/betterx/bclib/mixin/common/BoneMealItemMixin.java new file mode 100644 index 00000000..da0bc4b4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/BoneMealItemMixin.java @@ -0,0 +1,163 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BoneMealItem; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; + +import org.betterx.bclib.api.BonemealAPI; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.util.BlocksHelper; +import org.betterx.bclib.util.MHelper; +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; + +@Mixin(BoneMealItem.class) +public class BoneMealItemMixin { + private static final MutableBlockPos bclib_BLOCK_POS = new MutableBlockPos(); + + @Inject(method = "useOn", at = @At("HEAD"), cancellable = true) + private void bclib_onUse(UseOnContext context, CallbackInfoReturnable info) { + Level world = context.getLevel(); + BlockPos blockPos = context.getClickedPos(); + if (!world.isClientSide) { + BlockPos offseted = blockPos.relative(context.getClickedFace()); + if (BonemealAPI.isTerrain(world.getBlockState(blockPos).getBlock())) { + boolean consume = false; + if (BonemealAPI.isSpreadableTerrain(world.getBlockState(blockPos).getBlock())) { + BlockState terrain = bclib_getSpreadable(world, blockPos); + if (terrain != null) { + BlocksHelper.setWithoutUpdate(world, blockPos, terrain); + consume = true; + } + } else { + BlockState stateAbove = world.getBlockState(blockPos.above()); + if (!stateAbove.getFluidState().isEmpty()) { + if (stateAbove.is(Blocks.WATER)) { + consume = bclib_growWaterGrass(world, blockPos); + } + } else if (stateAbove.isAir()) { + consume = bclib_growLandGrass(world, blockPos); + } + } + if (consume) { + if (!context.getPlayer().isCreative()) { + context.getItemInHand().shrink(1); + } + world.levelEvent(2005, blockPos, 0); + info.setReturnValue(InteractionResult.SUCCESS); + info.cancel(); + } + } + } + } + + private boolean bclib_growLandGrass(Level world, BlockPos pos) { + int y1 = pos.getY() + 3; + int y2 = pos.getY() - 3; + boolean result = false; + for (int i = 0; i < 64; i++) { + int x = (int) (pos.getX() + world.random.nextGaussian() * 2); + int z = (int) (pos.getZ() + world.random.nextGaussian() * 2); + bclib_BLOCK_POS.setX(x); + bclib_BLOCK_POS.setZ(z); + for (int y = y1; y >= y2; y--) { + bclib_BLOCK_POS.setY(y); + BlockPos down = bclib_BLOCK_POS.below(); + if (world.isEmptyBlock(bclib_BLOCK_POS) && !world.isEmptyBlock(down)) { + BlockState grass = bclib_getLandGrassState(world, down); + if (grass != null) { + BlocksHelper.setWithoutUpdate(world, bclib_BLOCK_POS, grass); + result = true; + } + break; + } + } + } + return result; + } + + private boolean bclib_growWaterGrass(Level world, BlockPos pos) { + int y1 = pos.getY() + 3; + int y2 = pos.getY() - 3; + boolean result = false; + for (int i = 0; i < 64; i++) { + int x = (int) (pos.getX() + world.random.nextGaussian() * 2); + int z = (int) (pos.getZ() + world.random.nextGaussian() * 2); + bclib_BLOCK_POS.setX(x); + bclib_BLOCK_POS.setZ(z); + for (int y = y1; y >= y2; y--) { + bclib_BLOCK_POS.setY(y); + BlockPos down = bclib_BLOCK_POS.below(); + if (BlocksHelper.isFluid(world.getBlockState(bclib_BLOCK_POS)) && !BlocksHelper.isFluid(world.getBlockState( + down))) { + BlockState grass = bclib_getWaterGrassState(world, down); + if (grass != null) { + BlocksHelper.setWithoutUpdate(world, bclib_BLOCK_POS, grass); + result = true; + } + break; + } + } + } + return result; + } + + private BlockState bclib_getLandGrassState(Level world, BlockPos pos) { + BlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + block = BonemealAPI.getLandGrass(BiomeAPI.getBiomeID(world.getBiome(pos)), block, world.getRandom()); + return block == null ? null : block.defaultBlockState(); + } + + private BlockState bclib_getWaterGrassState(Level world, BlockPos pos) { + BlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + block = BonemealAPI.getWaterGrass(BiomeAPI.getBiomeID(world.getBiome(pos)), block, world.getRandom()); + return block == null ? null : block.defaultBlockState(); + } + + private BlockState bclib_getSpreadable(Level world, BlockPos pos) { + Vec3i[] offsets = MHelper.getOffsets(world.getRandom()); + BlockState center = world.getBlockState(pos); + for (Vec3i dir : offsets) { + BlockPos p = pos.offset(dir); + BlockState state = world.getBlockState(p); + Block terrain = BonemealAPI.getSpreadable(state.getBlock()); + if (center.is(terrain)) { + if (bclib_haveSameProperties(state, center)) { + for (Property property : center.getProperties()) { + state = state.setValue(property, center.getValue(property)); + } + } + return state; + } + } + return null; + } + + private boolean bclib_haveSameProperties(BlockState state1, BlockState state2) { + Property[] properties1 = state1.getProperties().toArray(new Property[0]); + Property[] properties2 = state2.getProperties().toArray(new Property[0]); + if (properties1.length != properties2.length) { + return false; + } + for (int i = 0; i < properties1.length; i++) { + String name1 = properties1[i].getName(); + String name2 = properties2[i].getName(); + if (!name1.equals(name2)) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/mixin/common/ChunkGeneratorMixin.java b/src/main/java/org/betterx/bclib/mixin/common/ChunkGeneratorMixin.java new file mode 100644 index 00000000..969b6eac --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/ChunkGeneratorMixin.java @@ -0,0 +1,42 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.Registry; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.structure.StructureSet; + +import org.betterx.bclib.interfaces.ChunkGeneratorAccessor; +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.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChunkGenerator.class) +public class ChunkGeneratorMixin implements ChunkGeneratorAccessor { + @Shadow + @Final + protected Registry structureSets; + private int bclib_featureIteratorSeed; + + @ModifyArg(method = "applyBiomeDecoration", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/WorldgenRandom;setFeatureSeed(JII)V")) + private long bclib_updateFeatureSeed(long seed) { + return Long.rotateRight(seed, bclib_featureIteratorSeed++); + } + + @Inject(method = "applyBiomeDecoration", at = @At("HEAD")) + private void bclib_obBiomeGenerate(WorldGenLevel worldGenLevel, + ChunkAccess chunkAccess, + StructureManager structureFeatureManager, + CallbackInfo ci) { + bclib_featureIteratorSeed = 0; + } + + public Registry bclib_getStructureSetsRegistry() { + return structureSets; + } +} diff --git a/src/main/java/ru/bclib/mixin/common/ComposterBlockAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/ComposterBlockAccessor.java similarity index 58% rename from src/main/java/ru/bclib/mixin/common/ComposterBlockAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/ComposterBlockAccessor.java index 9aad31dc..48ffe51f 100644 --- a/src/main/java/ru/bclib/mixin/common/ComposterBlockAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/ComposterBlockAccessor.java @@ -1,14 +1,15 @@ -package ru.bclib.mixin.common; - -import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.block.ComposterBlock; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(ComposterBlock.class) -public interface ComposterBlockAccessor { - @Invoker - static void callAdd(float levelIncreaseChance, ItemLike item) { - throw new AssertionError("@Invoker dummy body called"); - } -} +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.ComposterBlock; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ComposterBlock.class) +public interface ComposterBlockAccessor { + @Invoker + static void callAdd(float levelIncreaseChance, ItemLike item) { + throw new AssertionError("@Invoker dummy body called"); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/CraftingMenuMixin.java b/src/main/java/org/betterx/bclib/mixin/common/CraftingMenuMixin.java similarity index 52% rename from src/main/java/ru/bclib/mixin/common/CraftingMenuMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/CraftingMenuMixin.java index e07bcfcd..0d130627 100644 --- a/src/main/java/ru/bclib/mixin/common/CraftingMenuMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/CraftingMenuMixin.java @@ -1,31 +1,32 @@ -package ru.bclib.mixin.common; +package org.betterx.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.betterx.bclib.api.tag.CommonBlockTags; 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.tag.CommonBlockTags; @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 info) { - if (access.evaluate((world, pos) -> { - BlockState state = world.getBlockState(pos); - return state.getBlock() instanceof CraftingTableBlock || state.is(CommonBlockTags.WORKBENCHES); - }, true)) { - info.setReturnValue(true); - } - } + @Final + @Shadow + private ContainerLevelAccess access; + + @Inject(method = "stillValid", at = @At("HEAD"), cancellable = true) + private void bclib_stillValid(Player player, CallbackInfoReturnable info) { + if (access.evaluate((world, pos) -> { + BlockState state = world.getBlockState(pos); + return state.getBlock() instanceof CraftingTableBlock || state.is(CommonBlockTags.WORKBENCHES); + }, true)) { + info.setReturnValue(true); + } + } } diff --git a/src/main/java/ru/bclib/mixin/common/DiggerItemAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/DiggerItemAccessor.java similarity index 90% rename from src/main/java/ru/bclib/mixin/common/DiggerItemAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/DiggerItemAccessor.java index a3a8f925..faa955f6 100644 --- a/src/main/java/ru/bclib/mixin/common/DiggerItemAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/DiggerItemAccessor.java @@ -1,8 +1,9 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.tags.TagKey; import net.minecraft.world.item.DiggerItem; import net.minecraft.world.level.block.Block; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/main/java/ru/bclib/mixin/common/DimensionTypeMixin.java b/src/main/java/org/betterx/bclib/mixin/common/DimensionTypeMixin.java similarity index 59% rename from src/main/java/ru/bclib/mixin/common/DimensionTypeMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/DimensionTypeMixin.java index 68994351..81c2ac5a 100644 --- a/src/main/java/ru/bclib/mixin/common/DimensionTypeMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/DimensionTypeMixin.java @@ -1,36 +1,31 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; -import com.mojang.serialization.Lifecycle; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.WritableRegistry; -import net.minecraft.data.worldgen.DimensionTypes; -import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; -import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -import net.minecraft.world.level.levelgen.presets.WorldPresets; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import ru.bclib.world.generator.BCLibEndBiomeSource; -import ru.bclib.world.generator.BCLibNetherBiomeSource; - -import java.util.OptionalInt; @Mixin(DimensionType.class) public class DimensionTypeMixin { -// @Inject( + // @Inject( // method = "defaultDimensions(Lnet/minecraft/core/RegistryAccess;JZ)Lnet/minecraft/core/Registry;", // locals = LocalCapture.CAPTURE_FAILHARD, // at = @At("TAIL") // ) - private static void bclib_updateDimensions(RegistryAccess registryAccess, long seed, boolean bl, CallbackInfoReturnable info, WritableRegistry writableRegistry, Registry registry, Registry biomeRegistry, Registry structureRegistry, Registry noiseSettingsRegistry, Registry noiseParamRegistry) { - //This probably moved to WorldPresets.bootstrap(); + private static void bclib_updateDimensions(RegistryAccess registryAccess, + long seed, + boolean bl, + CallbackInfoReturnable info, + WritableRegistry writableRegistry, + Registry registry, + Registry biomeRegistry, + Registry structureRegistry, + Registry noiseSettingsRegistry, + Registry noiseParamRegistry) { + //This probably moved to WorldPresets.bootstrap(); // int id = writableRegistry.getId(writableRegistry.get(LevelStem.NETHER)); // writableRegistry.registerOrOverride( // OptionalInt.of(id), @@ -63,5 +58,5 @@ public class DimensionTypeMixin { // ), // Lifecycle.stable() // ); - } + } } diff --git a/src/main/java/org/betterx/bclib/mixin/common/EnchantingTableBlockMixin.java b/src/main/java/org/betterx/bclib/mixin/common/EnchantingTableBlockMixin.java new file mode 100644 index 00000000..b10698bc --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/EnchantingTableBlockMixin.java @@ -0,0 +1,29 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EnchantmentTableBlock; + +import org.betterx.bclib.api.tag.CommonBlockTags; +import org.betterx.bclib.util.MethodReplace; +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; + +@Mixin(EnchantmentTableBlock.class) +public abstract class EnchantingTableBlockMixin extends Block { + public EnchantingTableBlockMixin(Properties settings) { + super(settings); + } + + @Inject(method = "isValidBookShelf(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/BlockPos;)Z", at = @At("HEAD"), cancellable = true) + private static void bclib_isBookshelf(Level level, + BlockPos blockPos, + BlockPos blockPos2, + CallbackInfoReturnable info) { + MethodReplace.addBlockReplace(Blocks.BOOKSHELF, state -> state.is(CommonBlockTags.BOOKSHELVES)); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/ItemStackMixin.java b/src/main/java/org/betterx/bclib/mixin/common/ItemStackMixin.java new file mode 100644 index 00000000..e1ea3359 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/ItemStackMixin.java @@ -0,0 +1,23 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +import org.betterx.bclib.util.MethodReplace; +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 java.util.function.Function; + +@Mixin(ItemStack.class) +public class ItemStackMixin { + @Inject(method = "is(Lnet/minecraft/world/item/Item;)Z", at = @At("HEAD"), cancellable = true) + private void bclib_replaceFunction(Item item, CallbackInfoReturnable info) { + Function replacement = MethodReplace.getItemReplace(item); + if (replacement != null) { + info.setReturnValue(replacement.apply(ItemStack.class.cast(this))); + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/LayerLightSectionStorageMixin.java b/src/main/java/org/betterx/bclib/mixin/common/LayerLightSectionStorageMixin.java new file mode 100644 index 00000000..9a0daa77 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/LayerLightSectionStorageMixin.java @@ -0,0 +1,35 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.chunk.DataLayer; +import net.minecraft.world.level.lighting.LayerLightSectionStorage; + +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; + +@Mixin(LayerLightSectionStorage.class) +public class LayerLightSectionStorageMixin { + @Shadow + protected DataLayer getDataLayer(long sectionPos, boolean cached) { + return null; + } + + @Inject(method = "getStoredLevel", at = @At(value = "HEAD"), cancellable = true) + private void bclib_lightFix(long blockPos, CallbackInfoReturnable info) { + try { + long pos = SectionPos.blockToSection(blockPos); + DataLayer dataLayer = this.getDataLayer(pos, true); + info.setReturnValue(dataLayer.get( + SectionPos.sectionRelative(BlockPos.getX(blockPos)), + SectionPos.sectionRelative(BlockPos.getY(blockPos)), + SectionPos.sectionRelative(BlockPos.getZ(blockPos)) + )); + } catch (Exception e) { + info.setReturnValue(0); + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/MainMixin.java b/src/main/java/org/betterx/bclib/mixin/common/MainMixin.java new file mode 100644 index 00000000..e7365400 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/MainMixin.java @@ -0,0 +1,61 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.server.Main; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import net.minecraft.world.level.storage.LevelStorageSource; + +import joptsimple.ArgumentAcceptingOptionSpec; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import org.betterx.bclib.api.LifeCycleAPI; +import org.betterx.bclib.api.datafixer.DataFixerAPI; +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; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +@Mixin(Main.class) +abstract public class MainMixin { + @Inject(method = "main", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/LevelStorageSource;createDefault(Ljava/nio/file/Path;)Lnet/minecraft/world/level/storage/LevelStorageSource;")) + private static void bclib_callServerFix(String[] args, CallbackInfo ci) { + OptionParser parser = new OptionParser(); + ArgumentAcceptingOptionSpec optionUniverse = parser.accepts("universe") + .withRequiredArg() + .defaultsTo(".", new String[0]); + ArgumentAcceptingOptionSpec optionWorld = parser.accepts("world").withRequiredArg(); + + //this is only for compat reasons, we do not need to read thise options in our mixin, but it seems to cause + //errors if they are not defined + parser.accepts("nogui"); + parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits"); + parser.accepts("demo"); + parser.accepts("bonusChest"); + parser.accepts("forceUpgrade"); + parser.accepts("eraseCache"); + parser.accepts("safeMode", "Loads level with vanilla datapack only"); + parser.accepts("help").forHelp(); + parser.accepts("singleplayer").withRequiredArg(); + parser.accepts("port").withRequiredArg().ofType(Integer.class).defaultsTo(-1, new Integer[0]); + parser.accepts("serverId").withRequiredArg(); + parser.accepts("jfrProfile"); + parser.nonOptions(); + + OptionSet options = parser.parse(args); + + Path settingPath = Paths.get("server.properties"); + DedicatedServerSettings settings = new DedicatedServerSettings(settingPath); + + File file = new File(options.valueOf(optionUniverse)); + String levelID = Optional.ofNullable(options.valueOf(optionWorld)).orElse(settings.getProperties().levelName); + + LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(file.toPath()); + DataFixerAPI.fixData(levelStorageSource, levelID, false, (didFix) -> {/* not called when showUI==false */}); + + LifeCycleAPI._runBeforeLevelLoad(); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/MinecraftServerMixin.java b/src/main/java/org/betterx/bclib/mixin/common/MinecraftServerMixin.java new file mode 100644 index 00000000..881b599b --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/MinecraftServerMixin.java @@ -0,0 +1,82 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldStem; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.server.level.progress.ChunkProgressListenerFactory; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.server.players.GameProfileCache; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; +import net.minecraft.world.level.storage.WorldData; + +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.datafixers.DataFixer; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.recipes.BCLRecipeManager; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.net.Proxy; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + @Shadow + private MinecraftServer.ReloadableResources resources; + + @Final + @Shadow + private Map, ServerLevel> levels; + + @Final + @Shadow + protected WorldData worldData; + + @Inject(method = "*", at = @At("TAIL")) + private void bclib_onServerInit(Thread thread, + LevelStorageAccess levelStorageAccess, + PackRepository packRepository, + WorldStem worldStem, + Proxy proxy, + DataFixer dataFixer, + MinecraftSessionService minecraftSessionService, + GameProfileRepository gameProfileRepository, + GameProfileCache gameProfileCache, + ChunkProgressListenerFactory chunkProgressListenerFactory, + CallbackInfo ci) { + DataExchangeAPI.prepareServerside(); + } + + @Inject(method = "reloadResources", at = @At(value = "RETURN"), cancellable = true) + private void bclib_reloadResources(Collection collection, + CallbackInfoReturnable> info) { + bclib_injectRecipes(); + } + + @Inject(method = "loadLevel", at = @At(value = "RETURN"), cancellable = true) + private void bclib_loadLevel(CallbackInfo info) { + bclib_injectRecipes(); + } + + private void bclib_injectRecipes() { + RecipeManagerAccessor accessor = (RecipeManagerAccessor) resources.managers().getRecipeManager(); + accessor.bclib_setRecipesByName(BCLRecipeManager.getMapByName(accessor.bclib_getRecipesByName())); + accessor.bclib_setRecipes(BCLRecipeManager.getMap(accessor.bclib_getRecipes())); + } + + @Inject(method = "createLevels", at = @At(value = "HEAD")) + private void bcl_createLevel(ChunkProgressListener chunkProgressListener, CallbackInfo ci) { + System.out.println(this.worldData); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/MobSpawnSettingsAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/MobSpawnSettingsAccessor.java similarity index 63% rename from src/main/java/ru/bclib/mixin/common/MobSpawnSettingsAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/MobSpawnSettingsAccessor.java index e3491b73..7207fcff 100644 --- a/src/main/java/ru/bclib/mixin/common/MobSpawnSettingsAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/MobSpawnSettingsAccessor.java @@ -1,9 +1,10 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.biome.MobSpawnSettings; import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @@ -12,10 +13,10 @@ import java.util.Map; @Mixin(MobSpawnSettings.class) public interface MobSpawnSettingsAccessor { - @Accessor("spawners") - Map> bcl_getSpawners(); - - @Accessor("spawners") - @Mutable - void bcl_setSpawners(Map> spawners); + @Accessor("spawners") + Map> bcl_getSpawners(); + + @Accessor("spawners") + @Mutable + void bcl_setSpawners(Map> spawners); } diff --git a/src/main/java/org/betterx/bclib/mixin/common/MultiPackResourceManagerMixin.java b/src/main/java/org/betterx/bclib/mixin/common/MultiPackResourceManagerMixin.java new file mode 100644 index 00000000..f15e0fc3 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/MultiPackResourceManagerMixin.java @@ -0,0 +1,35 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.server.packs.resources.Resource; + +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 java.util.Optional; + +@Mixin(MultiPackResourceManager.class) +public class MultiPackResourceManagerMixin { + private static final String[] BCLIB_MISSING_RESOURCES = new String[]{ + "dimension/the_end.json", + "dimension/the_nether.json", + "dimension_type/the_end.json", + "dimension_type/the_nether.json" + }; + + @Inject(method = "getResource", at = @At("HEAD"), cancellable = true) + private void bclib_hasResource(ResourceLocation resourceLocation, CallbackInfoReturnable> info) { + if (resourceLocation.getNamespace().equals("minecraft")) { + for (String key : BCLIB_MISSING_RESOURCES) { + if (resourceLocation.getPath().equals(key)) { + info.setReturnValue(Optional.empty()); + info.cancel(); + return; + } + } + } + } +} diff --git a/src/main/java/ru/bclib/mixin/common/NetherBiomeDataMixin.java b/src/main/java/org/betterx/bclib/mixin/common/NetherBiomeDataMixin.java similarity index 53% rename from src/main/java/ru/bclib/mixin/common/NetherBiomeDataMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/NetherBiomeDataMixin.java index 36f5933b..d07908a3 100644 --- a/src/main/java/ru/bclib/mixin/common/NetherBiomeDataMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/NetherBiomeDataMixin.java @@ -1,19 +1,23 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; -import net.fabricmc.fabric.impl.biome.NetherBiomeData; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Climate; + +import net.fabricmc.fabric.impl.biome.NetherBiomeData; + +import org.betterx.bclib.world.biomes.FabricBiomesData; 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; -import ru.bclib.world.biomes.FabricBiomesData; @Mixin(value = NetherBiomeData.class, remap = false) public class NetherBiomeDataMixin { - @Inject(method = "addNetherBiome", at = @At(value = "HEAD")) - private static void bclib_addNetherBiome(ResourceKey biome, Climate.ParameterPoint spawnNoisePoint, CallbackInfo info) { - FabricBiomesData.NETHER_BIOMES.add(biome); - } + @Inject(method = "addNetherBiome", at = @At(value = "HEAD")) + private static void bclib_addNetherBiome(ResourceKey biome, + Climate.ParameterPoint spawnNoisePoint, + CallbackInfo info) { + FabricBiomesData.NETHER_BIOMES.add(biome); + } } diff --git a/src/main/java/org/betterx/bclib/mixin/common/NoiseBasedChunkGeneratorMixin.java b/src/main/java/org/betterx/bclib/mixin/common/NoiseBasedChunkGeneratorMixin.java new file mode 100644 index 00000000..f024e8dc --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/NoiseBasedChunkGeneratorMixin.java @@ -0,0 +1,70 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.*; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.carver.CarvingContext; + +import org.betterx.bclib.interfaces.NoiseGeneratorSettingsProvider; +import org.betterx.bclib.interfaces.SurfaceProvider; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.lang.reflect.Constructor; +import java.util.Optional; + +@Mixin(NoiseBasedChunkGenerator.class) +public abstract class NoiseBasedChunkGeneratorMixin implements SurfaceProvider, NoiseGeneratorSettingsProvider { + @Final + @Shadow + protected Holder settings; + + @Final + @Shadow + private Aquifer.FluidPicker globalFluidPicker; + + private static final BlockState bclib_air = Blocks.AIR.defaultBlockState(); + private static Constructor bclib_constructor; + + @Override + public NoiseGeneratorSettings bclib_getNoiseGeneratorSettings() { + return settings.value(); + } + + @Shadow + protected abstract NoiseChunk createNoiseChunk(ChunkAccess chunkAccess, + StructureManager structureManager, + Blender blender, + RandomState randomState); + + @Override + @SuppressWarnings("deprecation") + public BlockState bclib_getSurface(BlockPos pos, Holder biome, ServerLevel level) { + ChunkAccess chunkAccess = level.getChunk(pos.getX() >> 4, pos.getZ() >> 4); + StructureManager structureManager = level.structureManager(); + NoiseBasedChunkGenerator generator = NoiseBasedChunkGenerator.class.cast(this); + RandomState randomState = level.getChunkSource().randomState(); + + NoiseChunk noiseChunk = chunkAccess.getOrCreateNoiseChunk(ca -> this.createNoiseChunk(ca, + structureManager, + Blender.empty(), + randomState)); + + CarvingContext carvingContext = new CarvingContext(generator, + level.registryAccess(), + chunkAccess.getHeightAccessorForGeneration(), + noiseChunk, + randomState, + this.settings.value().surfaceRule()); + Optional optional = carvingContext.topMaterial(bpos -> biome, chunkAccess, pos, false); + return optional.isPresent() ? optional.get() : bclib_air; + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/NoiseGeneratorSettingsMixin.java b/src/main/java/org/betterx/bclib/mixin/common/NoiseGeneratorSettingsMixin.java new file mode 100644 index 00000000..507789ae --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/NoiseGeneratorSettingsMixin.java @@ -0,0 +1,92 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; + +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.interfaces.SurfaceRuleProvider; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Mixin(NoiseGeneratorSettings.class) +public class NoiseGeneratorSettingsMixin implements SurfaceRuleProvider { + @Mutable + @Final + @Shadow + private SurfaceRules.RuleSource surfaceRule; + + private SurfaceRules.RuleSource bclib_originalSurfaceRule; + private final Set bclib_biomeSources = new HashSet<>(); + + private void bclib_updateCustomRules() { + bclib_setCustomRules(BiomeAPI.getRuleSources(bclib_biomeSources)); + } + + @Override + public void bclib_addBiomeSource(BiomeSource source) { + bclib_biomeSources.add(source); + bclib_updateCustomRules(); + } + + @Override + public void bclib_clearBiomeSources() { + bclib_biomeSources.clear(); + bclib_clearCustomRules(); + } + + private void bclib_clearCustomRules() { + if (bclib_originalSurfaceRule != null) { + this.surfaceRule = bclib_originalSurfaceRule; + bclib_originalSurfaceRule = null; + } + } + + private void bclib_setCustomRules(List rules) { + if (rules.size() == 0) { + bclib_clearCustomRules(); + return; + } + + RuleSource org = bclib_getOriginalSurfaceRule(); + if (org instanceof SurfaceRules.SequenceRuleSource sequenceRule) { + List currentSequence = sequenceRule.sequence(); + rules = rules.stream().filter(r -> currentSequence.indexOf(r) < 0).collect(Collectors.toList()); + rules.addAll(sequenceRule.sequence()); + } else { + rules.add(org); + } + + bclib_setSurfaceRule(SurfaceRules.sequence(rules.toArray(new RuleSource[rules.size()]))); + } + + void bclib_setSurfaceRule(SurfaceRules.RuleSource surfaceRule) { + if (bclib_originalSurfaceRule == null) { + bclib_originalSurfaceRule = this.surfaceRule; + } + this.surfaceRule = surfaceRule; + } + + RuleSource bclib_getOriginalSurfaceRule() { + if (bclib_originalSurfaceRule == null) { + return surfaceRule; + } + + return bclib_originalSurfaceRule; + } + +// @Inject(method = "surfaceRule", at = @At("HEAD"), cancellable = true) +// private void bclib_surfaceRule(CallbackInfoReturnable info) { +// if (bclib_surfaceRule != null) { +// info.setReturnValue(bclib_surfaceRule); +// } +// } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/PistonBaseBlockMixin.java b/src/main/java/org/betterx/bclib/mixin/common/PistonBaseBlockMixin.java new file mode 100644 index 00000000..bede826a --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/PistonBaseBlockMixin.java @@ -0,0 +1,30 @@ +package org.betterx.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.betterx.bclib.api.tag.CommonBlockTags; +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; + +@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 cir) { + if (blockState.is(CommonBlockTags.IMMOBILE)) { + cir.setReturnValue(false); + cir.cancel(); + } + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/PortalShapeMixin.java b/src/main/java/org/betterx/bclib/mixin/common/PortalShapeMixin.java new file mode 100644 index 00000000..97731672 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/PortalShapeMixin.java @@ -0,0 +1,43 @@ +package org.betterx.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.betterx.bclib.api.tag.CommonBlockTags; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@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(CommonBlockTags.NETHER_PORTAL_FRAME) || FRAME.test(state, getter, pos); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/PotionBrewingAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/PotionBrewingAccessor.java similarity index 61% rename from src/main/java/ru/bclib/mixin/common/PotionBrewingAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/PotionBrewingAccessor.java index ac5f7d11..34d5f054 100644 --- a/src/main/java/ru/bclib/mixin/common/PotionBrewingAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/PotionBrewingAccessor.java @@ -1,15 +1,16 @@ -package ru.bclib.mixin.common; - -import net.minecraft.world.item.Item; -import net.minecraft.world.item.alchemy.Potion; -import net.minecraft.world.item.alchemy.PotionBrewing; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(PotionBrewing.class) -public interface PotionBrewingAccessor { - @Invoker - static void callAddMix(Potion input, Item item, Potion output) { - throw new AssertionError("@Invoker dummy body called"); - } -} +package org.betterx.bclib.mixin.common; + +import net.minecraft.world.item.Item; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.item.alchemy.PotionBrewing; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(PotionBrewing.class) +public interface PotionBrewingAccessor { + @Invoker + static void callAddMix(Potion input, Item item, Potion output) { + throw new AssertionError("@Invoker dummy body called"); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/RecipeManagerAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/RecipeManagerAccessor.java new file mode 100644 index 00000000..d30a860a --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/RecipeManagerAccessor.java @@ -0,0 +1,26 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.item.crafting.RecipeType; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(RecipeManager.class) +public interface RecipeManagerAccessor { + @Accessor("recipes") + Map, Map>> bclib_getRecipes(); + + @Accessor("recipes") + void bclib_setRecipes(Map, Map>> recipes); + + @Accessor("byName") + Map> bclib_getRecipesByName(); + + @Accessor("byName") + void bclib_setRecipesByName(Map> recipes); +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/mixin/common/RecipeManagerMixin.java b/src/main/java/org/betterx/bclib/mixin/common/RecipeManagerMixin.java new file mode 100644 index 00000000..f88d3406 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/RecipeManagerMixin.java @@ -0,0 +1,34 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; + +import org.betterx.bclib.recipes.BCLRecipeManager; +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 java.util.Map; +import java.util.Optional; + +@Mixin(RecipeManager.class) +public abstract class RecipeManagerMixin { + @Shadow + private > Map> byType(RecipeType type) { + return null; + } + + @Inject(method = "getRecipeFor", at = @At(value = "HEAD"), cancellable = true) + private > void bclib_getRecipeFor(RecipeType type, + C inventory, + Level level, + CallbackInfoReturnable> info) { + info.setReturnValue(BCLRecipeManager.getSortedRecipe(type, inventory, level, this::byType)); + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/mixin/common/ServerLevelMixin.java b/src/main/java/org/betterx/bclib/mixin/common/ServerLevelMixin.java new file mode 100644 index 00000000..6344725b --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/ServerLevelMixin.java @@ -0,0 +1,86 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; +import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraft.world.level.storage.WritableLevelData; + +import org.betterx.bclib.api.LifeCycleAPI; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.world.generator.BCLBiomeSource; +import org.betterx.bclib.world.generator.BCLibNetherBiomeSource; +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; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +@Mixin(ServerLevel.class) +public abstract class ServerLevelMixin extends Level { + private static String bclib_lastWorld = null; + + protected ServerLevelMixin(WritableLevelData writableLevelData, + ResourceKey resourceKey, + Holder holder, + Supplier supplier, + boolean bl, + boolean bl2, + long l, + int i) { + super(writableLevelData, resourceKey, holder, supplier, bl, bl2, l, i); + } + + + @Inject(method = "*", at = @At("TAIL")) + private void bclib_onServerWorldInit(MinecraftServer server, + Executor executor, + LevelStorageAccess levelStorageAccess, + ServerLevelData serverLevelData, + ResourceKey resourceKey, + LevelStem levelStem, + ChunkProgressListener chunkProgressListener, + boolean bl, + long l, + List list, + boolean bl2, + CallbackInfo ci) { + ServerLevel level = ServerLevel.class.cast(this); + LifeCycleAPI._runLevelLoad(level, + server, + executor, + levelStorageAccess, + serverLevelData, + resourceKey, + chunkProgressListener, + bl, + l, + list, + bl2); + + BiomeAPI.applyModifications(ServerLevel.class.cast(this)); + + if (level.dimension() == Level.NETHER) { + BCLibNetherBiomeSource.setWorldHeight(level.getChunkSource().getGenerator().getGenDepth()); + } + if (levelStem.generator().getBiomeSource() instanceof BCLBiomeSource source) { + source.setSeed(level.getSeed()); + } + + if (bclib_lastWorld != null && bclib_lastWorld.equals(levelStorageAccess.getLevelId())) { + return; + } + + bclib_lastWorld = levelStorageAccess.getLevelId(); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/ShovelItemAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/ShovelItemAccessor.java similarity index 61% rename from src/main/java/ru/bclib/mixin/common/ShovelItemAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/ShovelItemAccessor.java index 8103ba3f..6f9bd859 100644 --- a/src/main/java/ru/bclib/mixin/common/ShovelItemAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/ShovelItemAccessor.java @@ -1,8 +1,9 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.world.item.ShovelItem; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -10,8 +11,8 @@ import java.util.Map; @Mixin(ShovelItem.class) public interface ShovelItemAccessor { - @Accessor("FLATTENABLES") - static Map bclib_getFlattenables() { - throw new AssertionError("@Accessor dummy body called"); - } + @Accessor("FLATTENABLES") + static Map bclib_getFlattenables() { + throw new AssertionError("@Accessor dummy body called"); + } } diff --git a/src/main/java/ru/bclib/mixin/common/StructuresAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/StructuresAccessor.java similarity index 92% rename from src/main/java/ru/bclib/mixin/common/StructuresAccessor.java rename to src/main/java/org/betterx/bclib/mixin/common/StructuresAccessor.java index e961c570..463ebc03 100644 --- a/src/main/java/ru/bclib/mixin/common/StructuresAccessor.java +++ b/src/main/java/org/betterx/bclib/mixin/common/StructuresAccessor.java @@ -1,9 +1,10 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.core.Holder; import net.minecraft.data.worldgen.Structures; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.levelgen.structure.Structure; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/main/java/org/betterx/bclib/mixin/common/SurfaceRulesContextAccessor.java b/src/main/java/org/betterx/bclib/mixin/common/SurfaceRulesContextAccessor.java new file mode 100644 index 00000000..3b01b6cf --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/SurfaceRulesContextAccessor.java @@ -0,0 +1,48 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.Holder; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.NoiseChunk; +import net.minecraft.world.level.levelgen.SurfaceRules; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.function.Supplier; + +@Mixin(SurfaceRules.Context.class) +public interface SurfaceRulesContextAccessor { + @Accessor("blockX") + int getBlockX(); + + @Accessor("blockY") + int getBlockY(); + + @Accessor("blockZ") + int getBlockZ(); + + @Accessor("surfaceDepth") + int getSurfaceDepth(); + + @Accessor("biome") + Supplier> getBiome(); + + @Accessor("chunk") + ChunkAccess getChunk(); + + @Accessor("noiseChunk") + NoiseChunk getNoiseChunk(); + + @Accessor("stoneDepthAbove") + int getStoneDepthAbove(); + + @Accessor("stoneDepthBelow") + int getStoneDepthBelow(); + + @Accessor("lastUpdateY") + long getLastUpdateY(); + + @Accessor("lastUpdateXZ") + long getLastUpdateXZ(); +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/TagLoaderMixin.java b/src/main/java/org/betterx/bclib/mixin/common/TagLoaderMixin.java new file mode 100644 index 00000000..5b5375ad --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/TagLoaderMixin.java @@ -0,0 +1,26 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.Tag; +import net.minecraft.tags.TagLoader; + +import org.betterx.bclib.api.tag.TagAPI; +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.ModifyArg; + +import java.util.Map; + +@Mixin(TagLoader.class) +public class TagLoaderMixin { + @Final + @Shadow + private String directory; + + @ModifyArg(method = "loadAndBuild", at = @At(value = "INVOKE", target = "Lnet/minecraft/tags/TagLoader;build(Ljava/util/Map;)Ljava/util/Map;")) + public Map be_modifyTags(Map tagsMap) { + return TagAPI.apply(directory, tagsMap); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/TheEndBiomeDataMixin.java b/src/main/java/org/betterx/bclib/mixin/common/TheEndBiomeDataMixin.java new file mode 100644 index 00000000..d1e70603 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/TheEndBiomeDataMixin.java @@ -0,0 +1,45 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; + +import net.fabricmc.fabric.impl.biome.TheEndBiomeData; + +import org.betterx.bclib.world.biomes.FabricBiomesData; +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(value = TheEndBiomeData.class, remap = false) +public class TheEndBiomeDataMixin { + @Inject(method = "addEndBiomeReplacement", at = @At(value = "HEAD")) + private static void bclib_addEndBiomeReplacement(ResourceKey replaced, + ResourceKey variant, + double weight, + CallbackInfo info) { + if (replaced == Biomes.END_BARRENS || replaced == Biomes.SMALL_END_ISLANDS) { + FabricBiomesData.END_VOID_BIOMES.put(variant, (float) weight); + } else { + FabricBiomesData.END_LAND_BIOMES.put(variant, (float) weight); + } + } + + @Inject(method = "addEndMidlandsReplacement", at = @At(value = "HEAD")) + private static void bclib_addEndMidlandsReplacement(ResourceKey highlands, + ResourceKey midlands, + double weight, + CallbackInfo info) { + FabricBiomesData.END_LAND_BIOMES.put(midlands, (float) weight); + } + + @Inject(method = "addEndBarrensReplacement", at = @At(value = "HEAD")) + private static void bclib_addEndBarrensReplacement(ResourceKey highlands, + ResourceKey barrens, + double weight, + CallbackInfo info) { + FabricBiomesData.END_LAND_BIOMES.put(barrens, (float) weight); + FabricBiomesData.END_VOID_BIOMES.put(barrens, (float) weight); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/WorldGenPropertiesMixin.java b/src/main/java/org/betterx/bclib/mixin/common/WorldGenPropertiesMixin.java similarity index 93% rename from src/main/java/ru/bclib/mixin/common/WorldGenPropertiesMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/WorldGenPropertiesMixin.java index e0270829..448bfbc8 100644 --- a/src/main/java/ru/bclib/mixin/common/WorldGenPropertiesMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/WorldGenPropertiesMixin.java @@ -1,4 +1,4 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.server.dedicated.DedicatedServerProperties; diff --git a/src/main/java/org/betterx/bclib/mixin/common/WorldGenRegionMixin.java b/src/main/java/org/betterx/bclib/mixin/common/WorldGenRegionMixin.java new file mode 100644 index 00000000..59ee6f81 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/WorldGenRegionMixin.java @@ -0,0 +1,27 @@ +package org.betterx.bclib.mixin.common; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.chunk.ChunkAccess; + +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; + +@Mixin(WorldGenRegion.class) +public class WorldGenRegionMixin { + @Final + @Shadow + private ChunkAccess center; + + @Inject(method = "ensureCanWrite", at = @At("HEAD"), cancellable = true) + private void be_alterBlockCheck(BlockPos blockPos, CallbackInfoReturnable info) { + int x = blockPos.getX() >> 4; + int z = blockPos.getZ() >> 4; + WorldGenRegion region = (WorldGenRegion) (Object) this; + info.setReturnValue(Math.abs(x - center.getPos().x) < 2 && Math.abs(z - center.getPos().z) < 2); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/WorldOpenFlowsMixin.java b/src/main/java/org/betterx/bclib/mixin/common/WorldOpenFlowsMixin.java similarity index 70% rename from src/main/java/ru/bclib/mixin/common/WorldOpenFlowsMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/WorldOpenFlowsMixin.java index 2e6444e7..fc58d7ca 100644 --- a/src/main/java/ru/bclib/mixin/common/WorldOpenFlowsMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/WorldOpenFlowsMixin.java @@ -1,34 +1,35 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows; import net.minecraft.core.RegistryAccess; import net.minecraft.server.ReloadableServerResources; -import net.minecraft.server.WorldStem; import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.WorldData; +import org.betterx.bclib.api.LifeCycleAPI; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; +import org.betterx.bclib.api.datafixer.DataFixerAPI; +import org.betterx.bclib.config.Configs; 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.CallbackInfo; -import ru.bclib.api.LifeCycleAPI; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.datafixer.DataFixerAPI; -import ru.bclib.config.Configs; @Mixin(WorldOpenFlows.class) public abstract class WorldOpenFlowsMixin { - @Shadow @Final private LevelStorageSource levelSource; + @Shadow + @Final + private LevelStorageSource levelSource; - @Shadow protected abstract void doLoadLevel(Screen screen, String levelID, boolean safeMode, boolean canAskForBackup); + @Shadow + protected abstract void doLoadLevel(Screen screen, String levelID, boolean safeMode, boolean canAskForBackup); @Inject(method = "loadLevel", cancellable = true, at = @At("HEAD")) private void bcl_callFixerOnLoad(Screen screen, String levelID, CallbackInfo ci) { @@ -41,8 +42,7 @@ public abstract class WorldOpenFlowsMixin { })) { //cancel call when fix-screen is presented ci.cancel(); - } - else { + } else { LifeCycleAPI._runBeforeLevelLoad(); if (Configs.CLIENT_CONFIG.suppressExperimentalDialog()) { this.doLoadLevel(screen, levelID, false, false); @@ -52,12 +52,12 @@ public abstract class WorldOpenFlowsMixin { } } - @Inject(method="createFreshLevel", at=@At("HEAD")) + @Inject(method = "createFreshLevel", at = @At("HEAD")) public void bcl_createFreshLevel(String levelID, LevelSettings levelSettings, RegistryAccess registryAccess, WorldGenSettings worldGenSettings, - CallbackInfo ci){ + CallbackInfo ci) { DataExchangeAPI.prepareServerside(); BiomeAPI.prepareNewLevel(); @@ -65,12 +65,12 @@ public abstract class WorldOpenFlowsMixin { LifeCycleAPI._runBeforeLevelLoad(); } - @Inject(method="createLevelFromExistingSettings", at=@At("HEAD")) + @Inject(method = "createLevelFromExistingSettings", at = @At("HEAD")) public void bcl_createLevelFromExistingSettings(LevelStorageSource.LevelStorageAccess levelStorageAccess, - ReloadableServerResources reloadableServerResources, - RegistryAccess.Frozen frozen, - WorldData worldData, - CallbackInfo ci){ + ReloadableServerResources reloadableServerResources, + RegistryAccess.Frozen frozen, + WorldData worldData, + CallbackInfo ci) { DataExchangeAPI.prepareServerside(); BiomeAPI.prepareNewLevel(); diff --git a/src/main/java/ru/bclib/mixin/common/WorldPresetsBootstrapMixin.java b/src/main/java/org/betterx/bclib/mixin/common/WorldPresetsBootstrapMixin.java similarity index 59% rename from src/main/java/ru/bclib/mixin/common/WorldPresetsBootstrapMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/WorldPresetsBootstrapMixin.java index d19fdca5..7ac2f709 100644 --- a/src/main/java/ru/bclib/mixin/common/WorldPresetsBootstrapMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/WorldPresetsBootstrapMixin.java @@ -1,58 +1,65 @@ -package ru.bclib.mixin.common; +package org.betterx.bclib.mixin.common; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.data.BuiltinRegistries; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.MultiNoiseBiomeSource; -import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.presets.WorldPreset; import net.minecraft.world.level.levelgen.presets.WorldPresets; import net.minecraft.world.level.levelgen.structure.StructureSet; import net.minecraft.world.level.levelgen.synth.NormalNoise; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.world.generator.BCLibEndBiomeSource; +import org.betterx.bclib.world.generator.BCLibNetherBiomeSource; 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.ModifyArg; -import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import ru.bclib.BCLib; -import ru.bclib.api.LifeCycleAPI; -import ru.bclib.world.generator.BCLibEndBiomeSource; -import ru.bclib.world.generator.BCLibNetherBiomeSource; import java.util.Map; @Mixin(WorldPresets.Bootstrap.class) public abstract class WorldPresetsBootstrapMixin { private static final ResourceKey BCL_NORMAL = bcl_register("normal"); - @Shadow @Final private Registry presets; - @Shadow @Final private Registry biomes; - @Shadow @Final private Registry structureSets; - @Shadow @Final private Registry noises; - @Shadow @Final private Holder netherDimensionType; - @Shadow @Final private Holder netherNoiseSettings; - @Shadow @Final private Holder endDimensionType; - @Shadow @Final private Holder endNoiseSettings; + @Shadow + @Final + private Registry presets; + @Shadow + @Final + private Registry biomes; + @Shadow + @Final + private Registry structureSets; + @Shadow + @Final + private Registry noises; + @Shadow + @Final + private Holder netherDimensionType; + @Shadow + @Final + private Holder netherNoiseSettings; + @Shadow + @Final + private Holder endDimensionType; + @Shadow + @Final + private Holder endNoiseSettings; //see WorldPresets.register private static ResourceKey bcl_register(String string) { return ResourceKey.create(Registry.WORLD_PRESET_REGISTRY, BCLib.makeID(string)); } - @ModifyArg(method="run", at=@At(value="INVOKE", ordinal = 0, target="Lnet/minecraft/world/level/levelgen/presets/WorldPresets$Bootstrap;registerCustomOverworldPreset(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/dimension/LevelStem;)Lnet/minecraft/core/Holder;")) - private LevelStem bcl_getOverworldStem(LevelStem overworldStem){ + @ModifyArg(method = "run", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/world/level/levelgen/presets/WorldPresets$Bootstrap;registerCustomOverworldPreset(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/dimension/LevelStem;)Lnet/minecraft/core/Holder;")) + private LevelStem bcl_getOverworldStem(LevelStem overworldStem) { BCLibNetherBiomeSource netherSource = new BCLibNetherBiomeSource(this.biomes); BCLibEndBiomeSource endSource = new BCLibEndBiomeSource(this.biomes); @@ -73,7 +80,12 @@ public abstract class WorldPresetsBootstrapMixin { endSource, this.endNoiseSettings) ); - WorldPreset preset = new WorldPreset(Map.of(LevelStem.OVERWORLD, overworldStem, LevelStem.NETHER, bclNether, LevelStem.END, bclEnd)); + WorldPreset preset = new WorldPreset(Map.of(LevelStem.OVERWORLD, + overworldStem, + LevelStem.NETHER, + bclNether, + LevelStem.END, + bclEnd)); BuiltinRegistries.register(this.presets, BCL_NORMAL, preset); return overworldStem; diff --git a/src/main/java/org/betterx/bclib/mixin/common/shears/BeehiveBlockMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/BeehiveBlockMixin.java new file mode 100644 index 00000000..02ce7028 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/BeehiveBlockMixin.java @@ -0,0 +1,32 @@ +package org.betterx.bclib.mixin.common.shears; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BeehiveBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MethodReplace; +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; + +@Mixin(BeehiveBlock.class) +public class BeehiveBlockMixin { + @Inject(method = "use(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) + private void bclib_isShears(BlockState blockState, + Level level, + BlockPos blockPos, + Player player, + InteractionHand interactionHand, + BlockHitResult blockHitResult, + CallbackInfoReturnable info) { + MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/shears/DiggingEnchantmentMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/DiggingEnchantmentMixin.java similarity index 51% rename from src/main/java/ru/bclib/mixin/common/shears/DiggingEnchantmentMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/shears/DiggingEnchantmentMixin.java index 1bafcdab..3c32fe88 100644 --- a/src/main/java/ru/bclib/mixin/common/shears/DiggingEnchantmentMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/DiggingEnchantmentMixin.java @@ -1,17 +1,18 @@ -package ru.bclib.mixin.common.shears; +package org.betterx.bclib.mixin.common.shears; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.DiggingEnchantment; + +import org.betterx.bclib.items.tool.BaseShearsItem; 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.items.tool.BaseShearsItem; @Mixin(DiggingEnchantment.class) public class DiggingEnchantmentMixin { - @Inject(method = "canEnchant(Lnet/minecraft/world/item/ItemStack;)Z", at = @At("HEAD"), cancellable = true) - private void bclib_isShears(ItemStack itemStack, CallbackInfoReturnable info) { - if (BaseShearsItem.isShear(itemStack)) info.setReturnValue(true); - } + @Inject(method = "canEnchant(Lnet/minecraft/world/item/ItemStack;)Z", at = @At("HEAD"), cancellable = true) + private void bclib_isShears(ItemStack itemStack, CallbackInfoReturnable info) { + if (BaseShearsItem.isShear(itemStack)) info.setReturnValue(true); + } } diff --git a/src/main/java/ru/bclib/mixin/common/shears/ItemPredicateBuilderMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/ItemPredicateBuilderMixin.java similarity index 54% rename from src/main/java/ru/bclib/mixin/common/shears/ItemPredicateBuilderMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/shears/ItemPredicateBuilderMixin.java index 676126c1..c84c605f 100644 --- a/src/main/java/ru/bclib/mixin/common/shears/ItemPredicateBuilderMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/ItemPredicateBuilderMixin.java @@ -1,30 +1,33 @@ -package ru.bclib.mixin.common.shears; +package org.betterx.bclib.mixin.common.shears; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import org.jetbrains.annotations.Nullable; + +import org.betterx.bclib.api.tag.CommonItemTags; 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.tag.CommonItemTags; import java.util.Set; +import org.jetbrains.annotations.Nullable; @Mixin(ItemPredicate.class) public abstract class ItemPredicateBuilderMixin { - @Shadow @Final private @Nullable Set items; - - @Inject(method = "matches", at = @At("HEAD"), cancellable = true) - void bclib_isShears(ItemStack itemStack, CallbackInfoReturnable info) { - if (this.items != null && this.items.size() == 1 && this.items.contains(Items.SHEARS)) { - if (itemStack.is(CommonItemTags.SHEARS)) { - info.setReturnValue(true); - } - } - } + @Shadow + @Final + private @Nullable Set items; + + @Inject(method = "matches", at = @At("HEAD"), cancellable = true) + void bclib_isShears(ItemStack itemStack, CallbackInfoReturnable info) { + if (this.items != null && this.items.size() == 1 && this.items.contains(Items.SHEARS)) { + if (itemStack.is(CommonItemTags.SHEARS)) { + info.setReturnValue(true); + } + } + } } diff --git a/src/main/java/org/betterx/bclib/mixin/common/shears/MushroomCowMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/MushroomCowMixin.java new file mode 100644 index 00000000..787c6b47 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/MushroomCowMixin.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.mixin.common.shears; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.animal.MushroomCow; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Items; + +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MethodReplace; +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; + +@Mixin(MushroomCow.class) +public class MushroomCowMixin { + @Inject(method = "mobInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) + private void bclib_isShears(Player player, + InteractionHand interactionHand, + CallbackInfoReturnable info) { + MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/shears/PumpkinBlockMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/PumpkinBlockMixin.java similarity index 50% rename from src/main/java/ru/bclib/mixin/common/shears/PumpkinBlockMixin.java rename to src/main/java/org/betterx/bclib/mixin/common/shears/PumpkinBlockMixin.java index 56e6198a..21d7e7a0 100644 --- a/src/main/java/ru/bclib/mixin/common/shears/PumpkinBlockMixin.java +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/PumpkinBlockMixin.java @@ -1,4 +1,4 @@ -package ru.bclib.mixin.common.shears; +package org.betterx.bclib.mixin.common.shears; import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionHand; @@ -9,17 +9,24 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.PumpkinBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; + +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MethodReplace; 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.items.tool.BaseShearsItem; -import ru.bclib.util.MethodReplace; @Mixin(PumpkinBlock.class) public abstract class PumpkinBlockMixin { - @Inject(method = "use", at = @At("HEAD")) - private void bclib_isShears(BlockState blockState, Level level, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable info) { - MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); - } + @Inject(method = "use", at = @At("HEAD")) + private void bclib_isShears(BlockState blockState, + Level level, + BlockPos blockPos, + Player player, + InteractionHand interactionHand, + BlockHitResult blockHitResult, + CallbackInfoReturnable info) { + MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); + } } diff --git a/src/main/java/org/betterx/bclib/mixin/common/shears/SheepMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/SheepMixin.java new file mode 100644 index 00000000..01ce7572 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/SheepMixin.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.mixin.common.shears; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Items; + +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MethodReplace; +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; + +@Mixin(Sheep.class) +public class SheepMixin { + @Inject(method = "mobInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) + private void bclib_isShears(Player player, + InteractionHand interactionHand, + CallbackInfoReturnable info) { + MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/shears/SnowGolemMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/SnowGolemMixin.java new file mode 100644 index 00000000..2115d63c --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/SnowGolemMixin.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.mixin.common.shears; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.animal.SnowGolem; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Items; + +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MethodReplace; +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; + +@Mixin(SnowGolem.class) +public class SnowGolemMixin { + @Inject(method = "mobInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) + private void bclib_isShears(Player player, + InteractionHand interactionHand, + CallbackInfoReturnable info) { + MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); + } +} diff --git a/src/main/java/org/betterx/bclib/mixin/common/shears/TripWireBlockMixin.java b/src/main/java/org/betterx/bclib/mixin/common/shears/TripWireBlockMixin.java new file mode 100644 index 00000000..f5fe0be1 --- /dev/null +++ b/src/main/java/org/betterx/bclib/mixin/common/shears/TripWireBlockMixin.java @@ -0,0 +1,27 @@ +package org.betterx.bclib.mixin.common.shears; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.TripWireBlock; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.items.tool.BaseShearsItem; +import org.betterx.bclib.util.MethodReplace; +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(TripWireBlock.class) +public class TripWireBlockMixin { + @Inject(method = "playerWillDestroy(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/player/Player;)V", at = @At("HEAD")) + private void bclib_isShears(Level level, + BlockPos blockPos, + BlockState blockState, + Player player, + CallbackInfo info) { + MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); + } +} diff --git a/src/main/java/org/betterx/bclib/noise/OpenSimplexNoise.java b/src/main/java/org/betterx/bclib/noise/OpenSimplexNoise.java new file mode 100644 index 00000000..54f2fd6f --- /dev/null +++ b/src/main/java/org/betterx/bclib/noise/OpenSimplexNoise.java @@ -0,0 +1,2528 @@ +package org.betterx.bclib.noise; + +/* + * OpenSimplex Noise in Java. + * by Kurt Spencer + * + * v1.1 (October 5, 2014) + * - Added 2D and 4D implementations. + * - Proper gradient sets for all dimensions, from a + * dimensionally-generalizable scheme with an actual + * rhyme and reason behind it. + * - Removed default permutation array in favor of + * default seed. + * - Changed seed-based constructor to be independent + * of any particular randomization library, so results + * will be the same when ported to other languages. + */ + +public class OpenSimplexNoise { + private static final double STRETCH_CONSTANT_2D = -0.211324865405187; // (1/Math.sqrt(2+1)-1)/2; + private static final double SQUISH_CONSTANT_2D = 0.366025403784439; // (Math.sqrt(2+1)-1)/2; + private static final double STRETCH_CONSTANT_3D = -1.0 / 6; // (1/Math.sqrt(3+1)-1)/3; + private static final double SQUISH_CONSTANT_3D = 1.0 / 3; // (Math.sqrt(3+1)-1)/3; + private static final double STRETCH_CONSTANT_4D = -0.138196601125011; // (1/Math.sqrt(4+1)-1)/4; + private static final double SQUISH_CONSTANT_4D = 0.309016994374947; // (Math.sqrt(4+1)-1)/4; + + private static final double NORM_CONSTANT_2D = 47; + private static final double NORM_CONSTANT_3D = 103; + private static final double NORM_CONSTANT_4D = 30; + + private static final long DEFAULT_SEED = 0; + + private final short[] perm; + private final short[] permGradIndex3D; + + public OpenSimplexNoise() { + this(DEFAULT_SEED); + } + + public OpenSimplexNoise(short[] perm) { + this.perm = perm; + permGradIndex3D = new short[256]; + + for (int i = 0; i < 256; i++) { + // Since 3D has 24 gradients, simple bitmask won't work, so + // precompute modulo array. + permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3); + } + } + + // Initializes the class using a permutation array generated from a 64-bit + // seed. + // Generates a proper permutation (i.e. doesn't merely perform N successive + // pair swaps on a base array) + // Uses a simple 64-bit LCG. + public OpenSimplexNoise(long seed) { + perm = new short[256]; + permGradIndex3D = new short[256]; + short[] source = new short[256]; + for (short i = 0; i < 256; i++) { + source[i] = i; + } + seed = seed * 6364136223846793005l + 1442695040888963407l; + seed = seed * 6364136223846793005l + 1442695040888963407l; + seed = seed * 6364136223846793005l + 1442695040888963407l; + for (int i = 255; i >= 0; i--) { + seed = seed * 6364136223846793005l + 1442695040888963407l; + int r = (int) ((seed + 31) % (i + 1)); + if (r < 0) r += (i + 1); + perm[i] = source[r]; + permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3); + source[r] = source[i]; + } + } + + // 2D OpenSimplex Noise. + public double eval(double x, double y) { + + // Place input coordinates onto grid. + double stretchOffset = (x + y) * STRETCH_CONSTANT_2D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + + // Floor to get grid coordinates of rhombus (stretched square) + // super-cell origin. + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + + // Skew out to get actual coordinates of rhombus origin. We'll need + // these later. + double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + + // Compute grid coordinates relative to rhombus origin. + double xins = xs - xsb; + double yins = ys - ysb; + + // Sum those together to get a value that determines which region we're + // in. + double inSum = xins + yins; + + // Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + + // We'll be defining these inside the next block and using them + // afterwards. + double dx_ext, dy_ext; + int xsv_ext, ysv_ext; + + double value = 0; + + // Contribution (1,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_2D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_2D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1); + } + + // Contribution (0,1) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_2D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_2D; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2); + } + + if (inSum <= 1) { // We're inside the triangle (2-Simplex) at (0,0) + double zins = 1 - inSum; + if (zins > xins || zins > yins) { // (0,0) is one of the closest two + // triangular vertices + if (xins > yins) { + xsv_ext = xsb + 1; + ysv_ext = ysb - 1; + dx_ext = dx0 - 1; + dy_ext = dy0 + 1; + } else { + xsv_ext = xsb - 1; + ysv_ext = ysb + 1; + dx_ext = dx0 + 1; + dy_ext = dy0 - 1; + } + } else { // (1,0) and (0,1) are the closest two vertices. + xsv_ext = xsb + 1; + ysv_ext = ysb + 1; + dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; + } + } else { // We're inside the triangle (2-Simplex) at (1,1) + double zins = 2 - inSum; + if (zins < xins || zins < yins) { // (0,0) is one of the closest two + // triangular vertices + if (xins > yins) { + xsv_ext = xsb + 2; + ysv_ext = ysb + 0; + dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D; + } else { + xsv_ext = xsb + 0; + ysv_ext = ysb + 2; + dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D; + } + } else { // (1,0) and (0,1) are the closest two vertices. + dx_ext = dx0; + dy_ext = dy0; + xsv_ext = xsb; + ysv_ext = ysb; + } + xsb += 1; + ysb += 1; + dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; + dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; + } + + // Contribution (0,0) or (1,1) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0); + } + + // Extra Vertex + double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext; + if (attn_ext > 0) { + attn_ext *= attn_ext; + value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext); + } + + return value / NORM_CONSTANT_2D; + } + + // 3D OpenSimplex Noise. + public double eval(double x, double y, double z) { + + // Place input coordinates on simplectic honeycomb. + double stretchOffset = (x + y + z) * STRETCH_CONSTANT_3D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + + // Floor to get simplectic honeycomb coordinates of rhombohedron + // (stretched cube) super-cell origin. + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + int zsb = fastFloor(zs); + + // Skew out to get actual coordinates of rhombohedron origin. We'll need + // these later. + double squishOffset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + + // Compute simplectic honeycomb coordinates relative to rhombohedral + // origin. + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + + // Sum those together to get a value that determines which region we're + // in. + double inSum = xins + yins + zins; + + // Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + + // We'll be defining these inside the next block and using them + // afterwards. + double dx_ext0, dy_ext0, dz_ext0; + double dx_ext1, dy_ext1, dz_ext1; + int xsv_ext0, ysv_ext0, zsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1; + + double value = 0; + if (inSum <= 1) { // We're inside the tetrahedron (3-Simplex) at (0,0,0) + + // Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. + byte aPoint = 0x01; + double aScore = xins; + byte bPoint = 0x02; + double bScore = yins; + if (aScore >= bScore && zins > bScore) { + bScore = zins; + bPoint = 0x04; + } else if (aScore < bScore && zins > aScore) { + aScore = zins; + aPoint = 0x04; + } + + // Now we determine the two lattice points not part of the + // tetrahedron that may contribute. + // This depends on the closest two tetrahedral vertices, including + // (0,0,0) + double wins = 1 - inSum; + if (wins > aScore || wins > bScore) { // (0,0,0) is one of the + // closest two tetrahedral + // vertices. + byte c = (bScore > aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx0; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0; + if ((c & 0x01) == 0) { + ysv_ext1 -= 1; + dy_ext1 += 1; + } else { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0; + dz_ext1 = dz0 + 1; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1; + } + } else { // (0,0,0) is not one of the closest two tetrahedral + // vertices. + byte c = (byte) (aPoint | bPoint); // Our two extra vertices are + // determined by the closest + // two. + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + } + } + + // Contribution (0,0,0) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0); + } + + // Contribution (1,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + // Contribution (0,1,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; + double dz2 = dz1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + // Contribution (0,0,1) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + } else if (inSum >= 2) { // We're inside the tetrahedron (3-Simplex) at + // (1,1,1) + + // Determine which two tetrahedral vertices are the closest, out of + // (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). + byte aPoint = 0x06; + double aScore = xins; + byte bPoint = 0x05; + double bScore = yins; + if (aScore <= bScore && zins < bScore) { + bScore = zins; + bPoint = 0x03; + } else if (aScore > bScore && zins < aScore) { + aScore = zins; + aPoint = 0x03; + } + + // Now we determine the two lattice points not part of the + // tetrahedron that may contribute. + // This depends on the closest two tetrahedral vertices, including + // (1,1,1) + double wins = 3 - inSum; + if (wins < aScore || wins < bScore) { // (1,1,1) is one of the + // closest two tetrahedral + // vertices. + byte c = (bScore < aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + if ((c & 0x01) != 0) { + ysv_ext1 += 1; + dy_ext1 -= 1; + } else { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D; + } + } else { // (1,1,1) is not one of the closest two tetrahedral + // vertices. + byte c = (byte) (aPoint & bPoint); // Our two extra vertices are + // determined by the closest + // two. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx0 - SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysb + 1; + ysv_ext1 = ysb + 2; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy0 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz0 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + } + } + + // Contribution (1,1,0) + double dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3); + } + + // Contribution (1,0,1) + double dx2 = dx3; + double dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2); + } + + // Contribution (0,1,1) + double dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dy1 = dy3; + double dz1 = dz2; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1); + } + + // Contribution (1,1,1) + dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0); + } + } else { // We're inside the octahedron (Rectified 3-Simplex) in + // between. + double aScore; + byte aPoint; + boolean aIsFurtherSide; + double bScore; + byte bPoint; + boolean bIsFurtherSide; + + // Decide between point (0,0,1) and (1,1,0) as closest + double p1 = xins + yins; + if (p1 > 1) { + aScore = p1 - 1; + aPoint = 0x03; + aIsFurtherSide = true; + } else { + aScore = 1 - p1; + aPoint = 0x04; + aIsFurtherSide = false; + } + + // Decide between point (0,1,0) and (1,0,1) as closest + double p2 = xins + zins; + if (p2 > 1) { + bScore = p2 - 1; + bPoint = 0x05; + bIsFurtherSide = true; + } else { + bScore = 1 - p2; + bPoint = 0x02; + bIsFurtherSide = false; + } + + // The closest out of the two (1,0,0) and (0,1,1) will replace the + // furthest out of the two decided above, if closer. + double p3 = yins + zins; + if (p3 > 1) { + double score = p3 - 1; + if (aScore <= bScore && aScore < score) { + aScore = score; + aPoint = 0x06; + aIsFurtherSide = true; + } else if (aScore > bScore && bScore < score) { + bScore = score; + bPoint = 0x06; + bIsFurtherSide = true; + } + } else { + double score = 1 - p3; + if (aScore <= bScore && aScore < score) { + aScore = score; + aPoint = 0x01; + aIsFurtherSide = false; + } else if (aScore > bScore && bScore < score) { + bScore = score; + bPoint = 0x01; + bIsFurtherSide = false; + } + } + + // Where each of the two closest points are determines how the extra + // two vertices are calculated. + if (aIsFurtherSide == bIsFurtherSide) { + if (aIsFurtherSide) { // Both closest points on (1,1,1) side + + // One of the two extra points is (1,1,1) + dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + + // Other extra point is based on the shared axis. + byte c = (byte) (aPoint & bPoint); + if ((c & 0x01) != 0) { + dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 2; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + } else if ((c & 0x02) != 0) { + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb + 2; + zsv_ext1 = zsb; + } else { + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb + 2; + } + } else {// Both closest points on (0,0,0) side + + // One of the two extra points is (0,0,0) + dx_ext0 = dx0; + dy_ext0 = dy0; + dz_ext0 = dz0; + xsv_ext0 = xsb; + ysv_ext0 = ysb; + zsv_ext0 = zsb; + + // Other extra point is based on the omitted axis. + byte c = (byte) (aPoint | bPoint); + if ((c & 0x01) == 0) { + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb - 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb + 1; + } else if ((c & 0x02) == 0) { + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb - 1; + zsv_ext1 = zsb + 1; + } else { + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb - 1; + } + } + } else { // One point on (0,0,0) side, one point on (1,1,1) side + byte c1, c2; + if (aIsFurtherSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + // One contribution is a permutation of (1,1,-1) + if ((c1 & 0x01) == 0) { + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb - 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + } else if ((c1 & 0x02) == 0) { + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb - 1; + zsv_ext0 = zsb + 1; + } else { + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb - 1; + } + + // One contribution is a permutation of (0,0,2) + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + if ((c2 & 0x01) != 0) { + dx_ext1 -= 2; + xsv_ext1 += 2; + } else if ((c2 & 0x02) != 0) { + dy_ext1 -= 2; + ysv_ext1 += 2; + } else { + dz_ext1 -= 2; + zsv_ext1 += 2; + } + } + + // Contribution (1,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + // Contribution (0,1,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; + double dz2 = dz1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + // Contribution (0,0,1) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + + // Contribution (1,1,0) + double dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4); + } + + // Contribution (1,0,1) + double dx5 = dx4; + double dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5); + } + + // Contribution (0,1,1) + double dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dy6 = dy4; + double dz6 = dz5; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6); + } + } + + // First extra vertex + double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0; + if (attn_ext0 > 0) { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0); + } + + // Second extra vertex + double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1; + if (attn_ext1 > 0) { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1); + } + + return value / NORM_CONSTANT_3D; + } + + // 4D OpenSimplex Noise. + public double eval(double x, double y, double z, double w) { + + // Place input coordinates on simplectic honeycomb. + double stretchOffset = (x + y + z + w) * STRETCH_CONSTANT_4D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + double ws = w + stretchOffset; + + // Floor to get simplectic honeycomb coordinates of rhombo-hypercube + // super-cell origin. + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + int zsb = fastFloor(zs); + int wsb = fastFloor(ws); + + // Skew out to get actual coordinates of stretched rhombo-hypercube + // origin. We'll need these later. + double squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_CONSTANT_4D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + double wb = wsb + squishOffset; + + // Compute simplectic honeycomb coordinates relative to rhombo-hypercube + // origin. + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + double wins = ws - wsb; + + // Sum those together to get a value that determines which region we're + // in. + double inSum = xins + yins + zins + wins; + + // Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + double dw0 = w - wb; + + // We'll be defining these inside the next block and using them + // afterwards. + double dx_ext0, dy_ext0, dz_ext0, dw_ext0; + double dx_ext1, dy_ext1, dz_ext1, dw_ext1; + double dx_ext2, dy_ext2, dz_ext2, dw_ext2; + int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1; + int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2; + + double value = 0; + if (inSum <= 1) { // We're inside the pentachoron (4-Simplex) at + // (0,0,0,0) + + // Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) + // are closest. + byte aPoint = 0x01; + double aScore = xins; + byte bPoint = 0x02; + double bScore = yins; + if (aScore >= bScore && zins > bScore) { + bScore = zins; + bPoint = 0x04; + } else if (aScore < bScore && zins > aScore) { + aScore = zins; + aPoint = 0x04; + } + if (aScore >= bScore && wins > bScore) { + bScore = wins; + bPoint = 0x08; + } else if (aScore < bScore && wins > aScore) { + aScore = wins; + aPoint = 0x08; + } + + // Now we determine the three lattice points not part of the + // pentachoron that may contribute. + // This depends on the closest two pentachoron vertices, including + // (0,0,0,0) + double uins = 1 - inSum; + if (uins > aScore || uins > bScore) { // (0,0,0,0) is one of the + // closest two pentachoron + // vertices. + byte c = (bScore > aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx_ext2 = dx0; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0; + if ((c & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0; + if ((c & 0x03) != 0) { + if ((c & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw_ext1 = dw0; + dw_ext2 = dw0 + 1; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1; + } + } else { // (0,0,0,0) is not one of the closest two pentachoron + // vertices. + byte c = (byte) (aPoint | bPoint); // Our three extra vertices + // are determined by the + // closest two. + + if ((c & 0x01) == 0) { + xsv_ext0 = xsv_ext2 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext2 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0x01) { + ysv_ext1 -= 1; + dy_ext1 += 1; + } else { + ysv_ext2 -= 1; + dy_ext2 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0x03) { + zsv_ext1 -= 1; + dz_ext1 += 1; + } else { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - SQUISH_CONSTANT_4D; + dw_ext2 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw_ext2 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + } + + // Contribution (0,0,0,0) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0); + } + + // Contribution (1,0,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; + double dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + // Contribution (0,1,0,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; + double dz2 = dz1; + double dw2 = dw1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + // Contribution (0,0,1,0) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; + double dw3 = dw1; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + // Contribution (0,0,0,1) + double dx4 = dx2; + double dy4 = dy1; + double dz4 = dz1; + double dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + } else if (inSum >= 3) { // We're inside the pentachoron (4-Simplex) at + // (1,1,1,1) + // Determine which two of (1,1,1,0), + // (1,1,0,1), (1,0,1,1), (0,1,1,1) + // are closest. + byte aPoint = 0x0E; + double aScore = xins; + byte bPoint = 0x0D; + double bScore = yins; + if (aScore <= bScore && zins < bScore) { + bScore = zins; + bPoint = 0x0B; + } else if (aScore > bScore && zins < aScore) { + aScore = zins; + aPoint = 0x0B; + } + if (aScore <= bScore && wins < bScore) { + bScore = wins; + bPoint = 0x07; + } else if (aScore > bScore && wins < aScore) { + aScore = wins; + aPoint = 0x07; + } + + // Now we determine the three lattice points not part of the + // pentachoron that may contribute. + // This depends on the closest two pentachoron vertices, including + // (0,0,0,0) + double uins = 4 - inSum; + if (uins < aScore || uins < bScore) { // (1,1,1,1) is one of the + // closest two pentachoron + // vertices. + byte c = (bScore < aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 2 - 4 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + if ((c & 0x01) != 0) { + ysv_ext1 += 1; + dy_ext1 -= 1; + } else { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + if ((c & 0x03) != 0x03) { + if ((c & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext2 += 1; + dz_ext2 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw_ext1 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 - 4 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * SQUISH_CONSTANT_4D; + } + } else { // (1,1,1,1) is not one of the closest two pentachoron + // vertices. + byte c = (byte) (aPoint & bPoint); // Our three extra vertices + // are determined by the + // closest two. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsv_ext2 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext2 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x01) != 0) { + ysv_ext2 += 1; + dy_ext2 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x03) != 0) { + zsv_ext2 += 1; + dz_ext2 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw_ext2 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + } + + // Contribution (1,1,1,0) + double dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + // Contribution (1,1,0,1) + double dx3 = dx4; + double dy3 = dy4; + double dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; + double dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + // Contribution (1,0,1,1) + double dx2 = dx4; + double dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; + double dz2 = dz4; + double dw2 = dw3; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + // Contribution (0,1,1,1) + double dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; + double dz1 = dz4; + double dy1 = dy4; + double dw1 = dw3; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + // Contribution (1,1,1,1) + dx0 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + dy0 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + dz0 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw0 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0); + } + } else if (inSum <= 2) { // We're inside the first dispentachoron + // (Rectified 4-Simplex) + double aScore; + byte aPoint; + boolean aIsBiggerSide = true; + double bScore; + byte bPoint; + boolean bIsBiggerSide = true; + + // Decide between (1,1,0,0) and (0,0,1,1) + if (xins + yins > zins + wins) { + aScore = xins + yins; + aPoint = 0x03; + } else { + aScore = zins + wins; + aPoint = 0x0C; + } + + // Decide between (1,0,1,0) and (0,1,0,1) + if (xins + zins > yins + wins) { + bScore = xins + zins; + bPoint = 0x05; + } else { + bScore = yins + wins; + bPoint = 0x0A; + } + + // Closer between (1,0,0,1) and (0,1,1,0) will replace the further + // of a and b, if closer. + if (xins + wins > yins + zins) { + double score = xins + wins; + if (aScore >= bScore && score > bScore) { + bScore = score; + bPoint = 0x09; + } else if (aScore < bScore && score > aScore) { + aScore = score; + aPoint = 0x09; + } + } else { + double score = yins + zins; + if (aScore >= bScore && score > bScore) { + bScore = score; + bPoint = 0x06; + } else if (aScore < bScore && score > aScore) { + aScore = score; + aPoint = 0x06; + } + } + + // Decide if (1,0,0,0) is closer. + double p1 = 2 - inSum + xins; + if (aScore >= bScore && p1 > bScore) { + bScore = p1; + bPoint = 0x01; + bIsBiggerSide = false; + } else if (aScore < bScore && p1 > aScore) { + aScore = p1; + aPoint = 0x01; + aIsBiggerSide = false; + } + + // Decide if (0,1,0,0) is closer. + double p2 = 2 - inSum + yins; + if (aScore >= bScore && p2 > bScore) { + bScore = p2; + bPoint = 0x02; + bIsBiggerSide = false; + } else if (aScore < bScore && p2 > aScore) { + aScore = p2; + aPoint = 0x02; + aIsBiggerSide = false; + } + + // Decide if (0,0,1,0) is closer. + double p3 = 2 - inSum + zins; + if (aScore >= bScore && p3 > bScore) { + bScore = p3; + bPoint = 0x04; + bIsBiggerSide = false; + } else if (aScore < bScore && p3 > aScore) { + aScore = p3; + aPoint = 0x04; + aIsBiggerSide = false; + } + + // Decide if (0,0,0,1) is closer. + double p4 = 2 - inSum + wins; + if (aScore >= bScore && p4 > bScore) { + bScore = p4; + bPoint = 0x08; + bIsBiggerSide = false; + } else if (aScore < bScore && p4 > aScore) { + aScore = p4; + aPoint = 0x08; + aIsBiggerSide = false; + } + + // Where each of the two closest points are determines how the extra + // three vertices are calculated. + if (aIsBiggerSide == bIsBiggerSide) { + if (aIsBiggerSide) { // Both closest points on the bigger side + byte c1 = (byte) (aPoint | bPoint); + byte c2 = (byte) (aPoint & bPoint); + if ((c1 & 0x01) == 0) { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) == 0) { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 3 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 3 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + // One combination is a permutation of (0,0,0,2) based on c2 + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) != 0) { + xsv_ext2 += 2; + dx_ext2 -= 2; + } else if ((c2 & 0x02) != 0) { + ysv_ext2 += 2; + dy_ext2 -= 2; + } else if ((c2 & 0x04) != 0) { + zsv_ext2 += 2; + dz_ext2 -= 2; + } else { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + + } else { // Both closest points on the smaller side + // One of the two extra points is (0,0,0,0) + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0; + dy_ext2 = dy0; + dz_ext2 = dz0; + dw_ext2 = dw0; + + // Other two points are based on the omitted axes. + byte c = (byte) (aPoint | bPoint); + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + + } + } else { // One point on each "side" + byte c1, c2; + if (aIsBiggerSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + // Two contributions are the bigger-sided point with each 0 + // replaced with -1. + if ((c1 & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; + if ((c1 & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; + if ((c1 & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + + // One contribution is a permutation of (0,0,0,2) based on the + // smaller-sided point + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) != 0) { + xsv_ext2 += 2; + dx_ext2 -= 2; + } else if ((c2 & 0x02) != 0) { + ysv_ext2 += 2; + dy_ext2 -= 2; + } else if ((c2 & 0x04) != 0) { + zsv_ext2 += 2; + dz_ext2 -= 2; + } else { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + } + + // Contribution (1,0,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; + double dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + // Contribution (0,1,0,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; + double dz2 = dz1; + double dw2 = dw1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + // Contribution (0,0,1,0) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; + double dw3 = dw1; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + // Contribution (0,0,0,1) + double dx4 = dx2; + double dy4 = dy1; + double dz4 = dz1; + double dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + + // Contribution (1,1,0,0) + double dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + // Contribution (1,0,1,0) + double dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + // Contribution (1,0,0,1) + double dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + // Contribution (0,1,1,0) + double dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + // Contribution (0,1,0,1) + double dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + // Contribution (0,0,1,1) + double dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } else { // We're inside the second dispentachoron (Rectified 4-Simplex) + double aScore; + byte aPoint; + boolean aIsBiggerSide = true; + double bScore; + byte bPoint; + boolean bIsBiggerSide = true; + + // Decide between (0,0,1,1) and (1,1,0,0) + if (xins + yins < zins + wins) { + aScore = xins + yins; + aPoint = 0x0C; + } else { + aScore = zins + wins; + aPoint = 0x03; + } + + // Decide between (0,1,0,1) and (1,0,1,0) + if (xins + zins < yins + wins) { + bScore = xins + zins; + bPoint = 0x0A; + } else { + bScore = yins + wins; + bPoint = 0x05; + } + + // Closer between (0,1,1,0) and (1,0,0,1) will replace the further + // of a and b, if closer. + if (xins + wins < yins + zins) { + double score = xins + wins; + if (aScore <= bScore && score < bScore) { + bScore = score; + bPoint = 0x06; + } else if (aScore > bScore && score < aScore) { + aScore = score; + aPoint = 0x06; + } + } else { + double score = yins + zins; + if (aScore <= bScore && score < bScore) { + bScore = score; + bPoint = 0x09; + } else if (aScore > bScore && score < aScore) { + aScore = score; + aPoint = 0x09; + } + } + + // Decide if (0,1,1,1) is closer. + double p1 = 3 - inSum + xins; + if (aScore <= bScore && p1 < bScore) { + bScore = p1; + bPoint = 0x0E; + bIsBiggerSide = false; + } else if (aScore > bScore && p1 < aScore) { + aScore = p1; + aPoint = 0x0E; + aIsBiggerSide = false; + } + + // Decide if (1,0,1,1) is closer. + double p2 = 3 - inSum + yins; + if (aScore <= bScore && p2 < bScore) { + bScore = p2; + bPoint = 0x0D; + bIsBiggerSide = false; + } else if (aScore > bScore && p2 < aScore) { + aScore = p2; + aPoint = 0x0D; + aIsBiggerSide = false; + } + + // Decide if (1,1,0,1) is closer. + double p3 = 3 - inSum + zins; + if (aScore <= bScore && p3 < bScore) { + bScore = p3; + bPoint = 0x0B; + bIsBiggerSide = false; + } else if (aScore > bScore && p3 < aScore) { + aScore = p3; + aPoint = 0x0B; + aIsBiggerSide = false; + } + + // Decide if (1,1,1,0) is closer. + double p4 = 3 - inSum + wins; + if (aScore <= bScore && p4 < bScore) { + bScore = p4; + bPoint = 0x07; + bIsBiggerSide = false; + } else if (aScore > bScore && p4 < aScore) { + aScore = p4; + aPoint = 0x07; + aIsBiggerSide = false; + } + + // Where each of the two closest points are determines how the extra + // three vertices are calculated. + if (aIsBiggerSide == bIsBiggerSide) { + if (aIsBiggerSide) { // Both closest points on the bigger side + byte c1 = (byte) (aPoint & bPoint); + byte c2 = (byte) (aPoint | bPoint); + + // Two contributions are permutations of (0,0,0,1) and + // (0,0,0,2) based on c1 + xsv_ext0 = xsv_ext1 = xsb; + ysv_ext0 = ysv_ext1 = ysb; + zsv_ext0 = zsv_ext1 = zsb; + wsv_ext0 = wsv_ext1 = wsb; + dx_ext0 = dx0 - SQUISH_CONSTANT_4D; + dy_ext0 = dy0 - SQUISH_CONSTANT_4D; + dz_ext0 = dz0 - SQUISH_CONSTANT_4D; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c1 & 0x01) != 0) { + xsv_ext0 += 1; + dx_ext0 -= 1; + xsv_ext1 += 2; + dx_ext1 -= 2; + } else if ((c1 & 0x02) != 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + ysv_ext1 += 2; + dy_ext1 -= 2; + } else if ((c1 & 0x04) != 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + zsv_ext1 += 2; + dz_ext1 -= 2; + } else { + wsv_ext0 += 1; + dw_ext0 -= 1; + wsv_ext1 += 2; + dw_ext1 -= 2; + } + + // One contribution is a permutation of (1,1,1,-1) based on + // c2 + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) == 0) { + xsv_ext2 -= 2; + dx_ext2 += 2; + } else if ((c2 & 0x02) == 0) { + ysv_ext2 -= 2; + dy_ext2 += 2; + } else if ((c2 & 0x04) == 0) { + zsv_ext2 -= 2; + dz_ext2 += 2; + } else { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } else { // Both closest points on the smaller side + // One of the two extra points is (1,1,1,1) + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + + // Other two points are based on the shared axes. + byte c = (byte) (aPoint & bPoint); + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + } + } else { // One point on each "side" + byte c1, c2; + if (aIsBiggerSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + // Two contributions are the bigger-sided point with each 1 + // replaced with 2. + if ((c1 & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c1 & 0x01) == 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c1 & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) != 0) { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + + // One contribution is a permutation of (1,1,1,-1) based on the + // smaller-sided point + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) == 0) { + xsv_ext2 -= 2; + dx_ext2 += 2; + } else if ((c2 & 0x02) == 0) { + ysv_ext2 -= 2; + dy_ext2 += 2; + } else if ((c2 & 0x04) == 0) { + zsv_ext2 -= 2; + dz_ext2 += 2; + } else { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } + + // Contribution (1,1,1,0) + double dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + // Contribution (1,1,0,1) + double dx3 = dx4; + double dy3 = dy4; + double dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; + double dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + // Contribution (1,0,1,1) + double dx2 = dx4; + double dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; + double dz2 = dz4; + double dw2 = dw3; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + // Contribution (0,1,1,1) + double dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; + double dz1 = dz4; + double dy1 = dy4; + double dw1 = dw3; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + // Contribution (1,1,0,0) + double dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + // Contribution (1,0,1,0) + double dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + // Contribution (1,0,0,1) + double dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + // Contribution (0,1,1,0) + double dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + // Contribution (0,1,0,1) + double dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + // Contribution (0,0,1,1) + double dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } + + // First extra vertex + double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0; + if (attn_ext0 > 0) { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate( + xsv_ext0, + ysv_ext0, + zsv_ext0, + wsv_ext0, + dx_ext0, + dy_ext0, + dz_ext0, + dw_ext0 + ); + } + + // Second extra vertex + double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1; + if (attn_ext1 > 0) { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate( + xsv_ext1, + ysv_ext1, + zsv_ext1, + wsv_ext1, + dx_ext1, + dy_ext1, + dz_ext1, + dw_ext1 + ); + } + + // Third extra vertex + double attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2; + if (attn_ext2 > 0) { + attn_ext2 *= attn_ext2; + value += attn_ext2 * attn_ext2 * extrapolate( + xsv_ext2, + ysv_ext2, + zsv_ext2, + wsv_ext2, + dx_ext2, + dy_ext2, + dz_ext2, + dw_ext2 + ); + } + + return value / NORM_CONSTANT_4D; + } + + private double extrapolate(int xsb, int ysb, double dx, double dy) { + int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E; + return gradients2D[index] * dx + gradients2D[index + 1] * dy; + } + + private double extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) { + int index = permGradIndex3D[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF]; + return gradients3D[index] * dx + gradients3D[index + 1] * dy + gradients3D[index + 2] * dz; + } + + private double extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) { + int index = perm[(perm[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC; + return gradients4D[index] * dx + gradients4D[index + 1] * dy + gradients4D[index + 2] * dz + gradients4D[index + 3] * dw; + } + + private static int fastFloor(double x) { + int xi = (int) x; + return x < xi ? xi - 1 : xi; + } + + // Gradients for 2D. They approximate the directions to the + // vertices of an octagon from the center. + private static final byte[] gradients2D = new byte[]{5, 2, 2, 5, -5, 2, -2, 5, 5, -2, 2, -5, -5, -2, -2, -5,}; + + // Gradients for 3D. They approximate the directions to the + // vertices of a rhombicuboctahedron from the center, skewed so + // that the triangular and square facets can be inscribed inside + // circles of the same radius. + private static final byte[] gradients3D = new byte[]{ + -11, + 4, + 4, + -4, + 11, + 4, + -4, + 4, + 11, + 11, + 4, + 4, + 4, + 11, + 4, + 4, + 4, + 11, + -11, + -4, + 4, + -4, + -11, + 4, + -4, + -4, + 11, + 11, + -4, + 4, + 4, + -11, + 4, + 4, + -4, + 11, + -11, + 4, + -4, + -4, + 11, + -4, + -4, + 4, + -11, + 11, + 4, + -4, + 4, + 11, + -4, + 4, + 4, + -11, + -11, + -4, + -4, + -4, + -11, + -4, + -4, + -4, + -11, + 11, + -4, + -4, + 4, + -11, + -4, + 4, + -4, + -11, + }; + + // Gradients for 4D. They approximate the directions to the + // vertices of a disprismatotesseractihexadecachoron from the center, + // skewed so that the tetrahedral and cubic facets can be inscribed inside + // spheres of the same radius. + private static final byte[] gradients4D = new byte[]{ + 3, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 3, + -3, + 1, + 1, + 1, + -1, + 3, + 1, + 1, + -1, + 1, + 3, + 1, + -1, + 1, + 1, + 3, + 3, + -1, + 1, + 1, + 1, + -3, + 1, + 1, + 1, + -1, + 3, + 1, + 1, + -1, + 1, + 3, + -3, + -1, + 1, + 1, + -1, + -3, + 1, + 1, + -1, + -1, + 3, + 1, + -1, + -1, + 1, + 3, + 3, + 1, + -1, + 1, + 1, + 3, + -1, + 1, + 1, + 1, + -3, + 1, + 1, + 1, + -1, + 3, + -3, + 1, + -1, + 1, + -1, + 3, + -1, + 1, + -1, + 1, + -3, + 1, + -1, + 1, + -1, + 3, + 3, + -1, + -1, + 1, + 1, + -3, + -1, + 1, + 1, + -1, + -3, + 1, + 1, + -1, + -1, + 3, + -3, + -1, + -1, + 1, + -1, + -3, + -1, + 1, + -1, + -1, + -3, + 1, + -1, + -1, + -1, + 3, + 3, + 1, + 1, + -1, + 1, + 3, + 1, + -1, + 1, + 1, + 3, + -1, + 1, + 1, + 1, + -3, + -3, + 1, + 1, + -1, + -1, + 3, + 1, + -1, + -1, + 1, + 3, + -1, + -1, + 1, + 1, + -3, + 3, + -1, + 1, + -1, + 1, + -3, + 1, + -1, + 1, + -1, + 3, + -1, + 1, + -1, + 1, + -3, + -3, + -1, + 1, + -1, + -1, + -3, + 1, + -1, + -1, + -1, + 3, + -1, + -1, + -1, + 1, + -3, + 3, + 1, + -1, + -1, + 1, + 3, + -1, + -1, + 1, + 1, + -3, + -1, + 1, + 1, + -1, + -3, + -3, + 1, + -1, + -1, + -1, + 3, + -1, + -1, + -1, + 1, + -3, + -1, + -1, + 1, + -1, + -3, + 3, + -1, + -1, + -1, + 1, + -3, + -1, + -1, + 1, + -1, + -3, + -1, + 1, + -1, + -1, + -3, + -3, + -1, + -1, + -1, + -1, + -3, + -1, + -1, + -1, + -1, + -3, + -1, + -1, + -1, + -1, + -3, + }; +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/noise/VoronoiNoise.java b/src/main/java/org/betterx/bclib/noise/VoronoiNoise.java new file mode 100644 index 00000000..482dfe6e --- /dev/null +++ b/src/main/java/org/betterx/bclib/noise/VoronoiNoise.java @@ -0,0 +1,149 @@ +package org.betterx.bclib.noise; + +import net.minecraft.core.BlockPos; + +import org.betterx.bclib.util.MHelper; + +import java.util.Random; + +public class VoronoiNoise { + private static final Random RANDOM = new Random(); + final int seed; + + public VoronoiNoise() { + this(0); + } + + public VoronoiNoise(int seed) { + this.seed = seed; + } + + private int getSeed(int x, int y, int z) { + int h = seed + x * 374761393 + y * 668265263 + z; + h = (h ^ (h >> 13)) * 1274126177; + return h ^ (h >> 16); + } + + public double sample(double x, double y, double z) { + int ix = MHelper.floor(x); + int iy = MHelper.floor(y); + int iz = MHelper.floor(z); + + float px = (float) (x - ix); + float py = (float) (y - iy); + float pz = (float) (z - iz); + + float d = 10; + + for (int pox = -1; pox < 2; pox++) { + for (int poy = -1; poy < 2; poy++) { + for (int poz = -1; poz < 2; poz++) { + RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); + float pointX = pox + RANDOM.nextFloat(); + float pointY = poy + RANDOM.nextFloat(); + float pointZ = poz + RANDOM.nextFloat(); + float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); + if (d2 < d) { + d = d2; + } + } + } + } + + return Math.sqrt(d); + } + + public Random getRandom(double x, double y, double z) { + int ix = MHelper.floor(x); + int iy = MHelper.floor(y); + int iz = MHelper.floor(z); + + float px = (float) (x - ix); + float py = (float) (y - iy); + float pz = (float) (z - iz); + + float d = 10; + + int posX = 0; + int posY = 0; + int posZ = 0; + + for (int pox = -1; pox < 2; pox++) { + for (int poy = -1; poy < 2; poy++) { + for (int poz = -1; poz < 2; poz++) { + RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); + float pointX = pox + RANDOM.nextFloat(); + float pointY = poy + RANDOM.nextFloat(); + float pointZ = poz + RANDOM.nextFloat(); + float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); + if (d2 < d) { + d = d2; + posX = pox; + posY = poy; + posZ = poz; + } + } + } + } + + posX += ix; + posY += iy; + posZ += iz; + + int seed = MHelper.getSeed(posY, posX, posZ); + RANDOM.setSeed(seed); + + return RANDOM; + } + + public BlockPos[] getPos(double x, double y, double z, double scale) { + int ix = MHelper.floor(x); + int iy = MHelper.floor(y); + int iz = MHelper.floor(z); + + float px = (float) (x - ix); + float py = (float) (y - iy); + float pz = (float) (z - iz); + + float d = 10; + float selX = 0; + float selY = 0; + float selZ = 0; + float selXPre = 0; + float selYPre = 0; + float selZPre = 0; + + for (int pox = -1; pox < 2; pox++) { + for (int poy = -1; poy < 2; poy++) { + for (int poz = -1; poz < 2; poz++) { + RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); + float pointX = pox + RANDOM.nextFloat(); + float pointY = poy + RANDOM.nextFloat(); + float pointZ = poz + RANDOM.nextFloat(); + float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); + if (d2 < d) { + d = d2; + selXPre = selX; + selYPre = selY; + selZPre = selZ; + selX = pointX; + selY = pointY; + selZ = pointZ; + } + } + } + } + + BlockPos p1 = new BlockPos( + (ix + (double) selX) * scale, + (iy + (double) selY) * scale, + (iz + (double) selZ) * scale + ); + BlockPos p2 = new BlockPos( + (ix + (double) selXPre) * scale, + (iy + (double) selYPre) * scale, + (iz + (double) selZPre) * scale + ); + return new BlockPos[]{p1, p2}; + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java b/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java new file mode 100644 index 00000000..ac1f69cd --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java @@ -0,0 +1,349 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.core.NonNullList; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +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 net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.gson.JsonObject; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.tag.CommonItemTags; +import org.betterx.bclib.config.PathConfig; +import org.betterx.bclib.interfaces.UnknownReceipBookCategory; +import org.betterx.bclib.util.ItemUtil; +import org.betterx.bclib.util.RecipeHelper; + +import java.util.Objects; + +public class AnvilRecipe implements Recipe, UnknownReceipBookCategory { + public final static String GROUP = "smithing"; + public final static RecipeType 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); + + public static void register() { + + } + + 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); + //TODO: 1.18.2 Test if hammer still works + if (hammer.isEmpty() || !hammer.is(CommonItemTags.HAMMERS)) { + 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 getIngredients() { + NonNullList defaultedList = NonNullList.create(); + defaultedList.add(Ingredient.of(Registry.ITEM.stream() + .filter(item -> item.builtInRegistryHolder() + .is(CommonItemTags.HAMMERS)) + .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(TagKey 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) { + return; + } + + 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 { + @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); + } + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/BCLRecipeManager.java b/src/main/java/org/betterx/bclib/recipes/BCLRecipeManager.java new file mode 100644 index 00000000..84f4998e --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/BCLRecipeManager.java @@ -0,0 +1,114 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; +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 net.minecraft.world.level.block.Block; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import org.betterx.bclib.util.CollectionsUtil; + +import java.util.*; +import java.util.Map.Entry; +import java.util.function.Function; + +public class BCLRecipeManager { + private static final Map, Map>> RECIPES = Maps.newHashMap(); + private static final Map, Object> SORTED = Maps.newHashMap(); + private static final String MINECRAFT = "minecraft"; + + public static > Optional getSortedRecipe(RecipeType type, + C inventory, + Level level, + Function, Map>> getter) { + List> recipes = (List>) SORTED.computeIfAbsent(type, t -> { + Collection> values = getter.apply(type).values(); + List> list = new ArrayList<>(values); + list.sort((v1, v2) -> { + boolean b1 = v1.getId().getNamespace().equals(MINECRAFT); + boolean b2 = v2.getId().getNamespace().equals(MINECRAFT); + return b1 ^ b2 ? (b1 ? 1 : -1) : 0; + }); + return ImmutableList.copyOf(list); + }); + return (Optional) recipes.stream().filter(recipe -> recipe.matches(inventory, level)).findFirst(); + } + + public static void addRecipe(RecipeType type, Recipe recipe) { + Map> list = RECIPES.computeIfAbsent(type, i -> Maps.newHashMap()); + list.put(recipe.getId(), recipe); + } + + public static > T getRecipe(RecipeType type, ResourceLocation id) { + Map> map = RECIPES.get(type); + return map != null ? (T) map.get(id) : null; + } + + public static Map, Map>> getMap(Map, Map>> recipes) { + Map, Map>> result = Maps.newHashMap(); + + for (RecipeType type : recipes.keySet()) { + Map> typeList = Maps.newHashMap(); + typeList.putAll(recipes.get(type)); + result.put(type, typeList); + } + + SORTED.clear(); + RECIPES.forEach((type, list) -> { + if (list != null) { + Map> typeList = result.computeIfAbsent(type, i -> Maps.newHashMap()); + for (Entry> entry : list.entrySet()) { + ResourceLocation id = entry.getKey(); + typeList.computeIfAbsent(id, i -> entry.getValue()); + } + } + }); + + return result; + } + + public static Map> getMapByName(Map> recipes) { + Map> result = CollectionsUtil.getMutable(recipes); + RECIPES.values() + .forEach(map -> map.forEach((location, recipe) -> result.computeIfAbsent(location, i -> recipe))); + return result; + } + + public static , T extends Recipe> S registerSerializer(String modID, + String id, + S serializer) { + return Registry.register(Registry.RECIPE_SERIALIZER, modID + ":" + id, serializer); + } + + public static > RecipeType registerType(String modID, String type) { + ResourceLocation recipeTypeId = new ResourceLocation(modID, type); + return Registry.register(Registry.RECIPE_TYPE, recipeTypeId, new RecipeType() { + public String toString() { + return type; + } + }); + } + + 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; + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/recipes/CraftingRecipes.java b/src/main/java/org/betterx/bclib/recipes/CraftingRecipes.java new file mode 100644 index 00000000..c065d676 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/CraftingRecipes.java @@ -0,0 +1,88 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.tags.ItemTags; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Blocks; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.tag.CommonItemTags; +import org.betterx.bclib.config.Configs; + +public class CraftingRecipes { + public static void init() { + GridRecipe.make(BCLib.MOD_ID, "tag_smith_table", Blocks.SMITHING_TABLE) + .setShape("II", "##", "##") + .addMaterial('#', ItemTags.PLANKS) + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_cauldron", Blocks.CAULDRON) + .setShape("I I", "I I", "III") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_hopper", Blocks.HOPPER) + .setShape("I I", "ICI", " I ") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('C', CommonItemTags.CHEST) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_piston", Blocks.PISTON) + .setShape("WWW", "CIC", "CDC") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('D', Items.REDSTONE) + .addMaterial('C', Items.COBBLESTONE) + .addMaterial('W', ItemTags.PLANKS) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_rail", Blocks.RAIL) + .setOutputCount(16) + .setShape("I I", "ISI", "I I") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('S', Items.STICK) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_stonecutter", Blocks.STONECUTTER) + .setShape(" I ", "SSS") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('S', Items.STONE) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_bucket", Items.BUCKET) + .setShape("I I", " I ") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_compass", Items.COMPASS) + .setShape(" I ", "IDI", " I ") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('D', Items.REDSTONE) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_minecart", Items.MINECART) + .setShape("I I", "III") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + GridRecipe.make(BCLib.MOD_ID, "tag_shield", Items.SHIELD) + .setShape("WIW", "WWW", " W ") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('W', ItemTags.PLANKS) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + + GridRecipe.make(BCLib.MOD_ID, "tag_hopper", Blocks.HOPPER) + .setShape("I I", "ICI", " I ") + .addMaterial('I', CommonItemTags.IRON_INGOTS) + .addMaterial('C', CommonItemTags.CHEST) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + + GridRecipe.make(BCLib.MOD_ID, "tag_shulker_box", Blocks.SHULKER_BOX) + .setShape("S", "C", "S") + .addMaterial('S', Items.SHULKER_SHELL) + .addMaterial('C', CommonItemTags.CHEST) + .checkConfig(Configs.RECIPE_CONFIG) + .build(); + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java b/src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java new file mode 100644 index 00000000..fddf028f --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java @@ -0,0 +1,125 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.*; +import net.minecraft.world.level.ItemLike; + +import org.betterx.bclib.config.PathConfig; + +public class FurnaceRecipe { + private static final FurnaceRecipe INSTANCE = new FurnaceRecipe(); + + private ResourceLocation id; + private ItemLike input; + private ItemLike output; + private boolean exist; + private String group; + private int count; + private int time; + private float xp; + + private FurnaceRecipe() { + } + + public static FurnaceRecipe make(String modID, String name, ItemLike input, ItemLike output) { + INSTANCE.id = new ResourceLocation(modID, name); + INSTANCE.group = ""; + INSTANCE.input = input; + INSTANCE.output = output; + INSTANCE.count = 1; + INSTANCE.time = 200; + INSTANCE.xp = 0; + INSTANCE.exist = BCLRecipeManager.exists(output) && BCLRecipeManager.exists(input); + return INSTANCE; + } + + public FurnaceRecipe checkConfig(PathConfig config) { + exist &= config.getBoolean("furnace", id.getPath(), true); + return this; + } + + public FurnaceRecipe setGroup(String group) { + this.group = group; + return this; + } + + public FurnaceRecipe setOutputCount(int count) { + this.count = count; + return this; + } + + public FurnaceRecipe setXP(float xp) { + this.xp = xp; + return this; + } + + public FurnaceRecipe setCookTime(int time) { + this.time = time; + return this; + } + + public void build() { + build(false, false, false); + } + + public void buildWithBlasting() { + build(true, false, false); + } + + public void buildFoodlike() { + build(false, true, true); + } + + public void build(boolean blasting, boolean campfire, boolean smoker) { + if (!exist) { + return; + } + + SmeltingRecipe recipe = new SmeltingRecipe( + new ResourceLocation(id + "_smelting"), + group, + Ingredient.of(input), + new ItemStack(output, count), + xp, + time + ); + BCLRecipeManager.addRecipe(RecipeType.SMELTING, recipe); + + if (blasting) { + BlastingRecipe recipe2 = new BlastingRecipe( + new ResourceLocation(id + "_blasting"), + group, + Ingredient.of(input), + new ItemStack(output, count), + xp, + time / 2 + ); + BCLRecipeManager.addRecipe(RecipeType.BLASTING, recipe2); + } + + if (campfire) { + CampfireCookingRecipe recipe2 = new CampfireCookingRecipe( + new ResourceLocation(id + "_campfire"), + group, + Ingredient.of(input), + new ItemStack(output, count), + xp, + time * 3 + ); + BCLRecipeManager.addRecipe(RecipeType.CAMPFIRE_COOKING, recipe2); + } + + if (smoker) { + SmokingRecipe recipe2 = new SmokingRecipe( + new ResourceLocation(id + "_smoker"), + group, + Ingredient.of(input), + new ItemStack(output, count), + xp, + time / 2 + ); + BCLRecipeManager.addRecipe(RecipeType.SMOKING, recipe2); + } + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/GridRecipe.java b/src/main/java/org/betterx/bclib/recipes/GridRecipe.java new file mode 100644 index 00000000..c750fd03 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/GridRecipe.java @@ -0,0 +1,133 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.core.NonNullList; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.*; +import net.minecraft.world.level.ItemLike; + +import com.google.common.collect.Maps; +import org.betterx.bclib.config.PathConfig; + +import java.util.Arrays; +import java.util.Map; + +public class GridRecipe { + private static final GridRecipe INSTANCE = new GridRecipe(); + + private ResourceLocation id; + private ItemLike output; + + private String group; + private RecipeType type; + private boolean shaped; + private String[] shape; + private final Map materialKeys = Maps.newHashMap(); + private int count; + private boolean exist; + + private GridRecipe() { + } + + public static GridRecipe make(String modID, String name, ItemLike output) { + return make(new ResourceLocation(modID, name), output); + } + + public static GridRecipe make(ResourceLocation id, ItemLike output) { + INSTANCE.id = id; + INSTANCE.output = output; + + INSTANCE.group = ""; + INSTANCE.type = RecipeType.CRAFTING; + INSTANCE.shaped = true; + INSTANCE.shape = new String[]{"#"}; + INSTANCE.materialKeys.clear(); + INSTANCE.count = 1; + + INSTANCE.exist = output != null && BCLRecipeManager.exists(output); + + return INSTANCE; + } + + public GridRecipe checkConfig(PathConfig config) { + exist &= config.getBoolean("grid", id.getPath(), true); + return this; + } + + public GridRecipe setGroup(String group) { + this.group = group; + return this; + } + + public GridRecipe setShape(String... shape) { + this.shape = shape; + return this; + } + + public GridRecipe setList(String shape) { + this.shape = new String[]{shape}; + this.shaped = false; + return this; + } + + public GridRecipe addMaterial(char key, TagKey value) { + return addMaterial(key, Ingredient.of(value)); + } + + public GridRecipe addMaterial(char key, ItemStack... value) { + return addMaterial(key, Ingredient.of(Arrays.stream(value))); + } + + public GridRecipe addMaterial(char key, ItemLike... values) { + for (ItemLike item : values) { + exist &= BCLRecipeManager.exists(item); + } + return addMaterial(key, Ingredient.of(values)); + } + + private GridRecipe addMaterial(char key, Ingredient value) { + materialKeys.put(key, value); + return this; + } + + public GridRecipe setOutputCount(int count) { + this.count = count; + return this; + } + + private NonNullList getMaterials(int width, int height) { + NonNullList materials = NonNullList.withSize(width * height, Ingredient.EMPTY); + int pos = 0; + for (String line : shape) { + for (int i = 0; i < width; i++) { + char c = line.charAt(i); + Ingredient material = materialKeys.get(c); + materials.set(pos++, material == null ? Ingredient.EMPTY : material); + } + } + return materials; + } + + public void build() { + if (!exist) { + return; + } + + int height = shape.length; + int width = shape[0].length(); + ItemStack result = new ItemStack(output, count); + NonNullList materials = this.getMaterials(width, height); + + CraftingRecipe recipe = shaped ? new ShapedRecipe( + id, + group, + width, + height, + materials, + result + ) : new ShapelessRecipe(id, group, result, materials); + BCLRecipeManager.addRecipe(type, recipe); + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java b/src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java new file mode 100644 index 00000000..187e53d4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java @@ -0,0 +1,104 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.item.crafting.UpgradeRecipe; +import net.minecraft.world.level.ItemLike; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.config.PathConfig; + +public class SmithingTableRecipe { + + private final static SmithingTableRecipe BUILDER = new SmithingTableRecipe(); + private final static RecipeType TYPE = RecipeType.SMITHING; + + public static SmithingTableRecipe create(String modID, String name) { + return create(new ResourceLocation(modID, name)); + } + + public static SmithingTableRecipe create(ResourceLocation id) { + BUILDER.id = id; + BUILDER.base = null; + BUILDER.addition = null; + BUILDER.result = null; + BUILDER.exist = true; + + return BUILDER; + } + + private ResourceLocation id; + private Ingredient base; + private Ingredient addition; + private ItemStack result; + private boolean exist; + + private SmithingTableRecipe() { + } + + public SmithingTableRecipe checkConfig(PathConfig config) { + exist &= config.getBoolean("smithing", id.getPath(), true); + return this; + } + + public SmithingTableRecipe setResult(ItemLike item) { + return this.setResult(item, 1); + } + + public SmithingTableRecipe setResult(ItemLike item, int count) { + this.exist &= BCLRecipeManager.exists(item); + this.result = new ItemStack(item, count); + return this; + } + + public SmithingTableRecipe setBase(ItemLike... items) { + this.exist &= BCLRecipeManager.exists(items); + this.base = Ingredient.of(items); + return this; + } + + public SmithingTableRecipe setBase(TagKey tag) { + this.base = (Ingredient.of(tag)); + return this; + } + + public SmithingTableRecipe setAddition(ItemLike... items) { + this.exist &= BCLRecipeManager.exists(items); + this.addition = Ingredient.of(items); + return this; + } + + public SmithingTableRecipe setAddition(TagKey tag) { + this.addition = (Ingredient.of(tag)); + return this; + } + + public void build() { + if (!exist) { + return; + } + + if (base == null) { + BCLib.LOGGER.warning("Base input for Smithing recipe can't be 'null', recipe {} will be ignored!", id); + return; + } + if (addition == null) { + BCLib.LOGGER.warning("Addition input for Smithing recipe can't be 'null', recipe {} will be ignored!", id); + return; + } + if (result == null) { + BCLib.LOGGER.warning("Result for Smithing recipe can't be 'null', recipe {} will be ignored!", id); + return; + } + if (BCLRecipeManager.getRecipe(TYPE, id) != null) { + BCLib.LOGGER.warning("Can't add Smithing recipe! Id {} already exists!", id); + return; + } + + BCLRecipeManager.addRecipe(TYPE, new UpgradeRecipe(id, base, addition, result)); + } +} diff --git a/src/main/java/org/betterx/bclib/registry/BaseBlockEntities.java b/src/main/java/org/betterx/bclib/registry/BaseBlockEntities.java new file mode 100644 index 00000000..677d3dc0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/registry/BaseBlockEntities.java @@ -0,0 +1,61 @@ +package org.betterx.bclib.registry; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.blockentities.*; +import org.betterx.bclib.blockentities.DynamicBlockEntityType.BlockEntitySupplier; +import org.betterx.bclib.blocks.BaseBarrelBlock; +import org.betterx.bclib.blocks.BaseChestBlock; +import org.betterx.bclib.blocks.BaseFurnaceBlock; +import org.betterx.bclib.blocks.BaseSignBlock; + +public class BaseBlockEntities { + public static final DynamicBlockEntityType CHEST = registerBlockEntityType(BCLib.makeID( + "chest"), BaseChestBlockEntity::new); + public static final DynamicBlockEntityType BARREL = registerBlockEntityType(BCLib.makeID( + "barrel"), BaseBarrelBlockEntity::new); + public static final DynamicBlockEntityType SIGN = registerBlockEntityType(BCLib.makeID("sign"), + BaseSignBlockEntity::new); + public static final DynamicBlockEntityType FURNACE = registerBlockEntityType(BCLib.makeID( + "furnace"), BaseFurnaceBlockEntity::new); + + public static DynamicBlockEntityType registerBlockEntityType(ResourceLocation typeId, + BlockEntitySupplier supplier) { + return Registry.register(Registry.BLOCK_ENTITY_TYPE, typeId, new DynamicBlockEntityType<>(supplier)); + } + + public static void register() { + } + + public static Block[] getChests() { + return Registry.BLOCK + .stream() + .filter(block -> block instanceof BaseChestBlock) + .toArray(Block[]::new); + } + + public static Block[] getBarrels() { + return Registry.BLOCK + .stream() + .filter(block -> block instanceof BaseBarrelBlock) + .toArray(Block[]::new); + } + + public static Block[] getSigns() { + return Registry.BLOCK + .stream() + .filter(block -> block instanceof BaseSignBlock) + .toArray(Block[]::new); + } + + public static Block[] getFurnaces() { + return Registry.BLOCK + .stream() + .filter(block -> block instanceof BaseFurnaceBlock) + .toArray(Block[]::new); + } +} diff --git a/src/main/java/org/betterx/bclib/registry/BaseBlockEntityRenders.java b/src/main/java/org/betterx/bclib/registry/BaseBlockEntityRenders.java new file mode 100644 index 00000000..926a03fd --- /dev/null +++ b/src/main/java/org/betterx/bclib/registry/BaseBlockEntityRenders.java @@ -0,0 +1,16 @@ +package org.betterx.bclib.registry; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry; + +import org.betterx.bclib.client.render.BaseChestBlockEntityRenderer; +import org.betterx.bclib.client.render.BaseSignBlockEntityRenderer; + +@Environment(EnvType.CLIENT) +public class BaseBlockEntityRenders { + public static void register() { + BlockEntityRendererRegistry.register(BaseBlockEntities.CHEST, BaseChestBlockEntityRenderer::new); + BlockEntityRendererRegistry.register(BaseBlockEntities.SIGN, BaseSignBlockEntityRenderer::new); + } +} diff --git a/src/main/java/org/betterx/bclib/registry/BaseRegistry.java b/src/main/java/org/betterx/bclib/registry/BaseRegistry.java new file mode 100644 index 00000000..2970bfd6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/registry/BaseRegistry.java @@ -0,0 +1,82 @@ +package org.betterx.bclib.registry; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.betterx.bclib.config.PathConfig; + +import java.util.List; +import java.util.Map; + +public abstract class BaseRegistry { + private static final List> REGISTRIES = Lists.newArrayList(); + private static final Map> MOD_BLOCK_ITEMS = Maps.newHashMap(); + private static final Map> MOD_BLOCKS = Maps.newHashMap(); + private static final Map> 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> getRegisteredBlocks() { + return MOD_BLOCK_ITEMS; + } + + public static Map> getRegisteredItems() { + return MOD_ITEMS; + } + + public static List getModBlockItems(String modId) { + if (MOD_BLOCK_ITEMS.containsKey(modId)) { + return MOD_BLOCK_ITEMS.get(modId); + } + List modBlocks = Lists.newArrayList(); + MOD_BLOCK_ITEMS.put(modId, modBlocks); + return modBlocks; + } + + public static List getModItems(String modId) { + if (MOD_ITEMS.containsKey(modId)) { + return MOD_ITEMS.get(modId); + } + List modBlocks = Lists.newArrayList(); + MOD_ITEMS.put(modId, modBlocks); + return modBlocks; + } + + public static List getModBlocks(String modId) { + if (MOD_BLOCKS.containsKey(modId)) { + return MOD_BLOCKS.get(modId); + } + List modBlocks = Lists.newArrayList(); + MOD_BLOCKS.put(modId, modBlocks); + return modBlocks; + } + + public static void register() { + REGISTRIES.forEach(BaseRegistry::registerInternal); + } +} diff --git a/src/main/java/org/betterx/bclib/registry/BlockRegistry.java b/src/main/java/org/betterx/bclib/registry/BlockRegistry.java new file mode 100644 index 00000000..d8776b66 --- /dev/null +++ b/src/main/java/org/betterx/bclib/registry/BlockRegistry.java @@ -0,0 +1,90 @@ +package org.betterx.bclib.registry; + +import net.minecraft.core.Registry; +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 net.fabricmc.fabric.api.registry.FlammableBlockRegistry; + +import org.betterx.bclib.api.tag.*; +import org.betterx.bclib.blocks.BaseLeavesBlock; +import org.betterx.bclib.blocks.BaseOreBlock; +import org.betterx.bclib.blocks.FeatureSaplingBlock; +import org.betterx.bclib.config.PathConfig; +import org.betterx.bclib.interfaces.CustomItemProvider; + +public class BlockRegistry extends BaseRegistry { + 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()); + } else { + item = new BlockItem(block, makeItemSettings()); + } + registerBlockItem(id, item); + if (block.defaultBlockState().getMaterial().isFlammable() && FlammableBlockRegistry.getDefaultInstance() + .get(block) + .getBurnChance() == 0) { + FlammableBlockRegistry.getDefaultInstance().add(block, 5, 5); + } + + block = Registry.register(Registry.BLOCK, id, block); + getModBlocks(id.getNamespace()).add(block); + + if (block instanceof BaseLeavesBlock) { + TagAPI.addBlockTags( + block, + NamedBlockTags.LEAVES, + CommonBlockTags.LEAVES, + NamedMineableTags.HOE, + NamedMineableTags.SHEARS + ); + if (item != null) { + TagAPI.addItemTags(item, CommonItemTags.LEAVES, NamedItemTags.LEAVES); + } + } else if (block instanceof BaseOreBlock) { + TagAPI.addBlockTags(block, NamedMineableTags.PICKAXE); + } else if (block instanceof FeatureSaplingBlock) { + TagAPI.addBlockTags(block, CommonBlockTags.SAPLINGS, NamedBlockTags.SAPLINGS); + if (item != null) { + TagAPI.addItemTags(item, CommonItemTags.SAPLINGS, NamedItemTags.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); + 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); + } + } +} diff --git a/src/main/java/org/betterx/bclib/registry/ItemRegistry.java b/src/main/java/org/betterx/bclib/registry/ItemRegistry.java new file mode 100644 index 00000000..e8483062 --- /dev/null +++ b/src/main/java/org/betterx/bclib/registry/ItemRegistry.java @@ -0,0 +1,149 @@ +package org.betterx.bclib.registry; + +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.world.effect.MobEffectInstance; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.item.*; +import net.minecraft.world.level.block.DispenserBlock; + +import org.betterx.bclib.api.tag.CommonItemTags; +import org.betterx.bclib.api.tag.NamedToolTags; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.config.PathConfig; +import org.betterx.bclib.items.BaseDiscItem; +import org.betterx.bclib.items.BaseDrinkItem; +import org.betterx.bclib.items.BaseSpawnEggItem; +import org.betterx.bclib.items.ModelProviderItem; +import org.betterx.bclib.items.tool.BaseAxeItem; +import org.betterx.bclib.items.tool.BaseHoeItem; +import org.betterx.bclib.items.tool.BasePickaxeItem; +import org.betterx.bclib.items.tool.BaseShearsItem; + +public class ItemRegistry extends BaseRegistry { + public ItemRegistry(CreativeModeTab creativeTab, PathConfig config) { + super(creativeTab, config); + } + + 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; + } + register(itemId, item); + return item; + } + + public Item register(ResourceLocation itemId) { + return register(itemId, new ModelProviderItem(makeItemSettings())); + } + + @Override + public Item register(ResourceLocation itemId, Item item) { + if (!config.getBoolean("items", itemId.getPath(), true)) { + return item; + } + + registerItem(itemId, item); + + return item; + } + + public Item registerTool(ResourceLocation itemId, Item item) { + if (!config.getBoolean("tools", itemId.getPath(), true)) { + return item; + } + + registerItem(itemId, item); + + if (item instanceof ShovelItem) { + TagAPI.addItemTag(NamedToolTags.FABRIC_SHOVELS, item); + } else if (item instanceof SwordItem) { + TagAPI.addItemTag(NamedToolTags.FABRIC_SWORDS, item); + } else if (item instanceof BasePickaxeItem) { + TagAPI.addItemTag(NamedToolTags.FABRIC_PICKAXES, item); + } else if (item instanceof BaseAxeItem) { + TagAPI.addItemTag(NamedToolTags.FABRIC_AXES, item); + } else if (item instanceof BaseHoeItem) { + TagAPI.addItemTag(NamedToolTags.FABRIC_HOES, item); + } else if (item instanceof BaseShearsItem) { + TagAPI.addItemTags(item, NamedToolTags.FABRIC_SHEARS, CommonItemTags.SHEARS); + DispenserBlock.registerBehavior(item.asItem(), new ShearsDispenseItemBehavior()); + } + + return item; + } + + public Item registerEgg(ResourceLocation itemId, EntityType 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); + EntityType entityType = ((SpawnEggItem) stack.getItem()).getType(stack.getTag()); + entityType.spawn( + pointer.getLevel(), + stack, + null, + pointer.getPos().relative(direction), + MobSpawnType.DISPENSER, + direction != Direction.UP, + false + ); + stack.shrink(1); + return stack; + } + }; + DispenserBlock.registerBehavior(item, behavior); + return register(itemId, item); + } + + 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(itemId, builder.build()); + } + + public Item registerFood(ResourceLocation itemId, FoodProperties foodComponent) { + return register(itemId, new ModelProviderItem(makeItemSettings().food(foodComponent))); + } + + public Item registerDrink(ResourceLocation itemId, FoodProperties foodComponent) { + return register(itemId, new BaseDrinkItem(makeItemSettings().stacksTo(1).food(foodComponent))); + } + + public Item registerDrink(ResourceLocation itemId, int hunger, float saturation) { + FoodProperties.Builder builder = new FoodProperties.Builder().nutrition(hunger).saturationMod(saturation); + 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; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/PosInfo.java b/src/main/java/org/betterx/bclib/sdf/PosInfo.java new file mode 100644 index 00000000..97b5eb1a --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/PosInfo.java @@ -0,0 +1,104 @@ +package org.betterx.bclib.sdf; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.Map; + +public class PosInfo implements Comparable { + private static final BlockState AIR = Blocks.AIR.defaultBlockState(); + private final Map blocks; + private final Map add; + private final BlockPos pos; + private BlockState state; + + public static PosInfo create(Map blocks, Map add, BlockPos pos) { + return new PosInfo(blocks, add, pos); + } + + private PosInfo(Map blocks, Map add, BlockPos pos) { + this.blocks = blocks; + this.add = add; + this.pos = pos; + blocks.put(pos, this); + } + + public BlockState getState() { + return state; + } + + public BlockState getState(BlockPos pos) { + PosInfo info = blocks.get(pos); + if (info == null) { + info = add.get(pos); + return info == null ? AIR : info.getState(); + } + return info.getState(); + } + + public void setState(BlockState state) { + this.state = state; + } + + public void setState(BlockPos pos, BlockState state) { + PosInfo info = blocks.get(pos); + if (info != null) { + info.setState(state); + } + } + + public BlockState getState(Direction dir) { + PosInfo info = blocks.get(pos.relative(dir)); + if (info == null) { + info = add.get(pos.relative(dir)); + return info == null ? AIR : info.getState(); + } + return info.getState(); + } + + public BlockState getState(Direction dir, int distance) { + PosInfo info = blocks.get(pos.relative(dir, distance)); + if (info == null) { + return AIR; + } + return info.getState(); + } + + public BlockState getStateUp() { + return getState(Direction.UP); + } + + public BlockState getStateDown() { + return getState(Direction.DOWN); + } + + @Override + public int hashCode() { + return pos.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PosInfo)) { + return false; + } + return pos.equals(((PosInfo) obj).pos); + } + + @Override + public int compareTo(PosInfo info) { + return this.pos.getY() - info.pos.getY(); + } + + public BlockPos getPos() { + return pos; + } + + public void setBlockPos(BlockPos pos, BlockState state) { + PosInfo info = new PosInfo(blocks, add, pos); + info.state = state; + add.put(pos, info); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/SDF.java b/src/main/java/org/betterx/bclib/sdf/SDF.java new file mode 100644 index 00000000..3bf11404 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/SDF.java @@ -0,0 +1,306 @@ +package org.betterx.bclib.sdf; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.betterx.bclib.util.BlocksHelper; +import org.betterx.bclib.world.structures.StructureWorld; + +import java.util.*; +import java.util.function.Function; + +public abstract class SDF { + private final List> postProcesses = Lists.newArrayList(); + private Function canReplace = (state) -> { + return state.getMaterial().isReplaceable(); + }; + + public abstract float getDistance(float x, float y, float z); + + public abstract BlockState getBlockState(BlockPos pos); + + public SDF addPostProcess(Function postProcess) { + this.postProcesses.add(postProcess); + return this; + } + + public SDF setReplaceFunction(Function canReplace) { + this.canReplace = canReplace; + return this; + } + + public void fillRecursive(ServerLevelAccessor world, BlockPos start) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center : ends) { + for (Direction dir : Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + + if (!blocks.contains(bPos) && canReplace.apply(world.getBlockState(wpos))) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + BlockState state = getBlockState(wpos); + PosInfo.create(mapWorld, addInfo, wpos).setState(state); + add.add(bPos.immutable()); + } + } + } + } + + blocks.addAll(ends); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + List infos = new ArrayList(mapWorld.values()); + if (infos.size() > 0) { + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + if (canReplace.apply(world.getBlockState(info.getPos()))) { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + } + }); + } + } + + public void fillArea(ServerLevelAccessor world, BlockPos center, AABB box) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + + MutableBlockPos mut = new MutableBlockPos(); + for (int y = (int) box.minY; y <= box.maxY; y++) { + mut.setY(y); + for (int x = (int) box.minX; x <= box.maxX; x++) { + mut.setX(x); + for (int z = (int) box.minZ; z <= box.maxZ; z++) { + mut.setZ(z); + if (canReplace.apply(world.getBlockState(mut))) { + BlockPos fpos = mut.subtract(center); + if (this.getDistance(fpos.getX(), fpos.getY(), fpos.getZ()) < 0) { + PosInfo.create(mapWorld, addInfo, mut.immutable()).setState(getBlockState(mut)); + } + } + } + } + } + + List infos = new ArrayList(mapWorld.values()); + if (infos.size() > 0) { + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + if (canReplace.apply(world.getBlockState(info.getPos()))) { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + } + }); + } + } + + public void fillRecursiveIgnore(ServerLevelAccessor world, BlockPos start, Function ignore) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center : ends) { + for (Direction dir : Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + BlockState state = world.getBlockState(wpos); + boolean ign = ignore.apply(state); + if (!blocks.contains(bPos) && (ign || canReplace.apply(state))) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + PosInfo.create(mapWorld, addInfo, wpos).setState(ign ? state : getBlockState(bPos)); + add.add(bPos.immutable()); + } + } + } + } + + blocks.addAll(ends); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + List infos = new ArrayList(mapWorld.values()); + if (infos.size() > 0) { + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + if (canReplace.apply(world.getBlockState(info.getPos()))) { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + } + }); + } + } + + public void fillRecursive(StructureWorld world, BlockPos start) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center : ends) { + for (Direction dir : Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + + if (!blocks.contains(bPos)) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + BlockState state = getBlockState(wpos); + PosInfo.create(mapWorld, addInfo, wpos).setState(state); + add.add(bPos.immutable()); + } + } + } + } + + blocks.addAll(ends); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + List infos = new ArrayList(mapWorld.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + world.setBlock(info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + world.setBlock(info.getPos(), info.getState()); + }); + } + + public Set getPositions(ServerLevelAccessor world, BlockPos start) { + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center : ends) { + for (Direction dir : Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + BlockState state = world.getBlockState(wpos); + if (!blocks.contains(wpos) && canReplace.apply(state)) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + add.add(bPos.immutable()); + } + } + } + } + + ends.forEach((end) -> blocks.add(end.offset(start))); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + return blocks; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFBinary.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFBinary.java new file mode 100644 index 00000000..7ca523e6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFBinary.java @@ -0,0 +1,35 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.sdf.SDF; + +public abstract class SDFBinary extends SDF { + protected SDF sourceA; + protected SDF sourceB; + protected boolean firstValue; + + public SDFBinary setSourceA(SDF sourceA) { + this.sourceA = sourceA; + return this; + } + + public SDFBinary setSourceB(SDF sourceB) { + this.sourceB = sourceB; + return this; + } + + protected void selectValue(float a, float b) { + firstValue = a < b; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if (firstValue) { + return sourceA.getBlockState(pos); + } else { + return sourceB.getBlockState(pos); + } + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFCoordModify.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFCoordModify.java new file mode 100644 index 00000000..b2b4a638 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFCoordModify.java @@ -0,0 +1,22 @@ +package org.betterx.bclib.sdf.operator; + +import com.mojang.math.Vector3f; + +import java.util.function.Consumer; + +public class SDFCoordModify extends SDFUnary { + private final Vector3f pos = new Vector3f(); + private Consumer function; + + public SDFCoordModify setFunction(Consumer function) { + this.function = function; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + pos.set(x, y, z); + function.accept(pos); + return this.source.getDistance(pos.x(), pos.y(), pos.z()); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFCopyRotate.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFCopyRotate.java new file mode 100644 index 00000000..906fd97d --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFCopyRotate.java @@ -0,0 +1,19 @@ +package org.betterx.bclib.sdf.operator; + +import org.betterx.bclib.util.MHelper; + +public class SDFCopyRotate extends SDFUnary { + int count = 1; + + public SDFCopyRotate setCount(int count) { + this.count = count; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float px = (float) Math.atan2(x, z); + float pz = MHelper.length(x, z); + return this.source.getDistance(px, y, pz); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFDisplacement.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFDisplacement.java new file mode 100644 index 00000000..a5c2477d --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFDisplacement.java @@ -0,0 +1,21 @@ +package org.betterx.bclib.sdf.operator; + +import com.mojang.math.Vector3f; + +import java.util.function.Function; + +public class SDFDisplacement extends SDFUnary { + private final Vector3f pos = new Vector3f(); + private Function displace; + + public SDFDisplacement setFunction(Function displace) { + this.displace = displace; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + pos.set(x, y, z); + return this.source.getDistance(x, y, z) + displace.apply(pos); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFFlatWave.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFFlatWave.java new file mode 100644 index 00000000..efe7ec4a --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFFlatWave.java @@ -0,0 +1,28 @@ +package org.betterx.bclib.sdf.operator; + +public class SDFFlatWave extends SDFDisplacement { + private int rayCount = 1; + private float intensity; + private float angle; + + public SDFFlatWave() { + setFunction((pos) -> { + return (float) Math.cos(Math.atan2(pos.x(), pos.z()) * rayCount + angle) * intensity; + }); + } + + public SDFFlatWave setRaysCount(int count) { + this.rayCount = count; + return this; + } + + public SDFFlatWave setAngle(float angle) { + this.angle = angle; + return this; + } + + public SDFFlatWave setIntensity(float intensity) { + this.intensity = intensity; + return this; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFHeightmap.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFHeightmap.java new file mode 100644 index 00000000..0eb474ed --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFHeightmap.java @@ -0,0 +1,64 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +import com.mojang.blaze3d.platform.NativeImage; + +public class SDFHeightmap extends SDFDisplacement { + private float intensity = 1F; + private NativeImage map; + private float offsetX; + private float offsetZ; + private float scale; + private float cos = 1; + private float sin = 0; + + public SDFHeightmap() { + setFunction((pos) -> { + if (map == null) { + return 0F; + } + float px = Mth.clamp(pos.x() * scale + offsetX, 0, map.getWidth() - 2); + float pz = Mth.clamp(pos.z() * scale + offsetZ, 0, map.getHeight() - 2); + float dx = (px * cos - pz * sin); + float dz = (pz * cos + px * sin); + int x1 = Mth.floor(dx); + int z1 = Mth.floor(dz); + int x2 = x1 + 1; + int z2 = z1 + 1; + dx = dx - x1; + dz = dz - z1; + float a = (map.getPixelRGBA(x1, z1) & 255) / 255F; + float b = (map.getPixelRGBA(x2, z1) & 255) / 255F; + float c = (map.getPixelRGBA(x1, z2) & 255) / 255F; + float d = (map.getPixelRGBA(x2, z2) & 255) / 255F; + a = Mth.lerp(dx, a, b); + b = Mth.lerp(dx, c, d); + return -Mth.lerp(dz, a, b) * intensity; + }); + } + + public SDFHeightmap setMap(NativeImage map) { + this.map = map; + offsetX = map.getWidth() * 0.5F; + offsetZ = map.getHeight() * 0.5F; + scale = map.getWidth(); + return this; + } + + public SDFHeightmap setAngle(float angle) { + sin = Mth.sin(angle); + cos = Mth.cos(angle); + return this; + } + + public SDFHeightmap setScale(float scale) { + this.scale = map.getWidth() * scale; + return this; + } + + public SDFHeightmap setIntensity(float intensity) { + this.intensity = intensity; + return this; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFIntersection.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFIntersection.java new file mode 100644 index 00000000..3d6be14d --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFIntersection.java @@ -0,0 +1,13 @@ +package org.betterx.bclib.sdf.operator; + +import org.betterx.bclib.util.MHelper; + +public class SDFIntersection extends SDFBinary { + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + return MHelper.max(a, b); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFInvert.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFInvert.java new file mode 100644 index 00000000..8ce1dfe6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFInvert.java @@ -0,0 +1,8 @@ +package org.betterx.bclib.sdf.operator; + +public class SDFInvert extends SDFUnary { + @Override + public float getDistance(float x, float y, float z) { + return -this.source.getDistance(x, y, z); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFRadialNoiseMap.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFRadialNoiseMap.java new file mode 100644 index 00000000..82d50803 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFRadialNoiseMap.java @@ -0,0 +1,64 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +import org.betterx.bclib.noise.OpenSimplexNoise; +import org.betterx.bclib.util.MHelper; + +public class SDFRadialNoiseMap extends SDFDisplacement { + private static final float SIN = Mth.sin(0.5F); + private static final float COS = Mth.cos(0.5F); + + private OpenSimplexNoise noise; + private float intensity = 1F; + private float radius = 1F; + private short offsetX; + private short offsetZ; + + public SDFRadialNoiseMap() { + setFunction((pos) -> { + if (intensity == 0) { + return 0F; + } + float px = pos.x() / radius; + float pz = pos.z() / radius; + float distance = MHelper.lengthSqr(px, pz); + if (distance > 1) { + return 0F; + } + distance = 1 - Mth.sqrt(distance); + float nx = px * COS - pz * SIN; + float nz = pz * COS + px * SIN; + distance *= getNoise(nx * 0.75 + offsetX, nz * 0.75 + offsetZ); + return distance * intensity; + }); + } + + private float getNoise(double x, double z) { + return (float) noise.eval(x, z) + (float) noise.eval( + x * 3 + 1000, + z * 3 + ) * 0.5F + (float) noise.eval(x * 9 + 1000, z * 9) * 0.2F; + } + + public SDFRadialNoiseMap setSeed(long seed) { + noise = new OpenSimplexNoise(seed); + return this; + } + + public SDFRadialNoiseMap setIntensity(float intensity) { + this.intensity = intensity; + return this; + } + + public SDFRadialNoiseMap setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFRadialNoiseMap setOffset(int x, int z) { + offsetX = (short) (x & 32767); + offsetZ = (short) (z & 32767); + return this; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFRotation.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFRotation.java new file mode 100644 index 00000000..fb782d4c --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFRotation.java @@ -0,0 +1,21 @@ +package org.betterx.bclib.sdf.operator; + +import com.mojang.math.Quaternion; +import com.mojang.math.Vector3f; + +public class SDFRotation extends SDFUnary { + private final Vector3f pos = new Vector3f(); + private Quaternion rotation; + + public SDFRotation setRotation(Vector3f axis, float rotationAngle) { + rotation = new Quaternion(axis, rotationAngle, false); + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + pos.set(x, y, z); + pos.transform(rotation); + return source.getDistance(pos.x(), pos.y(), pos.z()); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFRound.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFRound.java new file mode 100644 index 00000000..36bd54b4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFRound.java @@ -0,0 +1,15 @@ +package org.betterx.bclib.sdf.operator; + +public class SDFRound extends SDFUnary { + private float radius; + + public SDFRound setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return this.source.getDistance(x, y, z) - radius; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFScale.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFScale.java new file mode 100644 index 00000000..7aa258f2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFScale.java @@ -0,0 +1,15 @@ +package org.betterx.bclib.sdf.operator; + +public class SDFScale extends SDFUnary { + private float scale; + + public SDFScale setScale(float scale) { + this.scale = scale; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return source.getDistance(x / scale, y / scale, z / scale) * scale; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFScale3D.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFScale3D.java new file mode 100644 index 00000000..6defc4aa --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFScale3D.java @@ -0,0 +1,19 @@ +package org.betterx.bclib.sdf.operator; + +public class SDFScale3D extends SDFUnary { + private float x; + private float y; + private float z; + + public SDFScale3D setScale(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return source.getDistance(x / this.x, y / this.y, z / this.z); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothIntersection.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothIntersection.java new file mode 100644 index 00000000..e10f784d --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothIntersection.java @@ -0,0 +1,21 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +public class SDFSmoothIntersection extends SDFBinary { + private float radius; + + public SDFSmoothIntersection setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + float h = Mth.clamp(0.5F - 0.5F * (b - a) / radius, 0F, 1F); + return Mth.lerp(h, b, a) + radius * h * (1F - h); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothSubtraction.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothSubtraction.java new file mode 100644 index 00000000..3d12385a --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothSubtraction.java @@ -0,0 +1,21 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +public class SDFSmoothSubtraction extends SDFBinary { + private float radius; + + public SDFSmoothSubtraction setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + float h = Mth.clamp(0.5F - 0.5F * (b + a) / radius, 0F, 1F); + return Mth.lerp(h, b, -a) + radius * h * (1F - h); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothUnion.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothUnion.java new file mode 100644 index 00000000..e09f22a2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFSmoothUnion.java @@ -0,0 +1,21 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +public class SDFSmoothUnion extends SDFBinary { + private float radius; + + public SDFSmoothUnion setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + float h = Mth.clamp(0.5F + 0.5F * (b - a) / radius, 0F, 1F); + return Mth.lerp(h, b, a) - radius * h * (1F - h); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFSubtraction.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFSubtraction.java new file mode 100644 index 00000000..345c5670 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFSubtraction.java @@ -0,0 +1,13 @@ +package org.betterx.bclib.sdf.operator; + +import org.betterx.bclib.util.MHelper; + +public class SDFSubtraction extends SDFBinary { + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + return MHelper.max(a, -b); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFTranslate.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFTranslate.java new file mode 100644 index 00000000..2bbc2c1c --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFTranslate.java @@ -0,0 +1,19 @@ +package org.betterx.bclib.sdf.operator; + +public class SDFTranslate extends SDFUnary { + float x; + float y; + float z; + + public SDFTranslate setTranslate(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return source.getDistance(x - this.x, y - this.y, z - this.z); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFUnary.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFUnary.java new file mode 100644 index 00000000..ed47fe6b --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFUnary.java @@ -0,0 +1,20 @@ +package org.betterx.bclib.sdf.operator; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.sdf.SDF; + +public abstract class SDFUnary extends SDF { + protected SDF source; + + public SDFUnary setSource(SDF source) { + this.source = source; + return this; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return source.getBlockState(pos); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/operator/SDFUnion.java b/src/main/java/org/betterx/bclib/sdf/operator/SDFUnion.java new file mode 100644 index 00000000..c461aa8f --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/operator/SDFUnion.java @@ -0,0 +1,13 @@ +package org.betterx.bclib.sdf.operator; + +import org.betterx.bclib.util.MHelper; + +public class SDFUnion extends SDFBinary { + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + return MHelper.min(a, b); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFCappedCone.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFCappedCone.java new file mode 100644 index 00000000..a5871f9b --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFCappedCone.java @@ -0,0 +1,44 @@ +package org.betterx.bclib.sdf.primitive; + +import net.minecraft.util.Mth; + +import org.betterx.bclib.util.MHelper; + +public class SDFCappedCone extends SDFPrimitive { + private float radius1; + private float radius2; + private float height; + + public SDFCappedCone setRadius1(float radius) { + this.radius1 = radius; + return this; + } + + public SDFCappedCone setRadius2(float radius) { + this.radius2 = radius; + return this; + } + + public SDFCappedCone setHeight(float height) { + this.height = height; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float qx = MHelper.length(x, z); + float k2x = radius2 - radius1; + float k2y = 2 * height; + float cax = qx - MHelper.min(qx, (y < 0F) ? radius1 : radius2); + float cay = Math.abs(y) - height; + float mlt = Mth.clamp( + MHelper.dot(radius2 - qx, height - y, k2x, k2y) / MHelper.dot(k2x, k2y, k2x, k2y), + 0F, + 1F + ); + float cbx = qx - radius2 + k2x * mlt; + float cby = y - height + k2y * mlt; + float s = (cbx < 0F && cay < 0F) ? -1F : 1F; + return s * (float) Math.sqrt(MHelper.min(MHelper.dot(cax, cay, cax, cay), MHelper.dot(cbx, cby, cbx, cby))); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFCapsule.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFCapsule.java new file mode 100644 index 00000000..70ccc9f9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFCapsule.java @@ -0,0 +1,25 @@ +package org.betterx.bclib.sdf.primitive; + +import net.minecraft.util.Mth; + +import org.betterx.bclib.util.MHelper; + +public class SDFCapsule extends SDFPrimitive { + private float radius; + private float height; + + public SDFCapsule setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFCapsule setHeight(float height) { + this.height = height; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return MHelper.length(x, y - Mth.clamp(y, 0, height), z) - radius; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFFlatland.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFFlatland.java new file mode 100644 index 00000000..a1aac88c --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFFlatland.java @@ -0,0 +1,8 @@ +package org.betterx.bclib.sdf.primitive; + +public class SDFFlatland extends SDFPrimitive { + @Override + public float getDistance(float x, float y, float z) { + return y; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFHexPrism.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFHexPrism.java new file mode 100644 index 00000000..ad2bd24a --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFHexPrism.java @@ -0,0 +1,26 @@ +package org.betterx.bclib.sdf.primitive; + +import org.betterx.bclib.util.MHelper; + +public class SDFHexPrism extends SDFPrimitive { + private float radius; + private float height; + + public SDFHexPrism setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFHexPrism setHeight(float height) { + this.height = height; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float px = Math.abs(x); + float py = Math.abs(y); + float pz = Math.abs(z); + return MHelper.max(py - height, MHelper.max((px * 0.866025F + pz * 0.5F), pz) - radius); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFLine.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFLine.java new file mode 100644 index 00000000..fc8fa476 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFLine.java @@ -0,0 +1,50 @@ +package org.betterx.bclib.sdf.primitive; + +import net.minecraft.util.Mth; + +import org.betterx.bclib.util.MHelper; + +public class SDFLine extends SDFPrimitive { + private float radius; + private float x1; + private float y1; + private float z1; + private float x2; + private float y2; + private float z2; + + public SDFLine setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFLine setStart(float x, float y, float z) { + this.x1 = x; + this.y1 = y; + this.z1 = z; + return this; + } + + public SDFLine setEnd(float x, float y, float z) { + this.x2 = x; + this.y2 = y; + this.z2 = z; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float pax = x - x1; + float pay = y - y1; + float paz = z - z1; + + float bax = x2 - x1; + float bay = y2 - y1; + float baz = z2 - z1; + + float dpb = MHelper.dot(pax, pay, paz, bax, bay, baz); + float dbb = MHelper.dot(bax, bay, baz, bax, bay, baz); + float h = Mth.clamp(dpb / dbb, 0F, 1F); + return MHelper.length(pax - bax * h, pay - bay * h, paz - baz * h) - radius; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFPie.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFPie.java new file mode 100644 index 00000000..1e087ab3 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFPie.java @@ -0,0 +1,32 @@ +package org.betterx.bclib.sdf.primitive; + +import net.minecraft.util.Mth; + +import org.betterx.bclib.util.MHelper; + +public class SDFPie extends SDFPrimitive { + private float sin; + private float cos; + private float radius; + + public SDFPie setAngle(float angle) { + this.sin = (float) Math.sin(angle); + this.cos = (float) Math.cos(angle); + return this; + } + + public SDFPie setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float px = Math.abs(x); + float l = MHelper.length(px, y, z) - radius; + float m = MHelper.dot(px, z, sin, cos); + m = Mth.clamp(m, 0, radius); + m = MHelper.length(px - sin * m, z - cos * m); + return MHelper.max(l, m * Math.signum(cos * px - sin * z)); + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFPrimitive.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFPrimitive.java new file mode 100644 index 00000000..743ff73f --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFPrimitive.java @@ -0,0 +1,40 @@ +package org.betterx.bclib.sdf.primitive; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import org.betterx.bclib.sdf.SDF; + +import java.util.function.Function; + +public abstract class SDFPrimitive extends SDF { + protected Function placerFunction; + + public SDFPrimitive setBlock(Function placerFunction) { + this.placerFunction = placerFunction; + return this; + } + + public SDFPrimitive setBlock(BlockState state) { + this.placerFunction = (pos) -> { + return state; + }; + return this; + } + + public SDFPrimitive setBlock(Block block) { + this.placerFunction = (pos) -> { + return block.defaultBlockState(); + }; + return this; + } + + public BlockState getBlockState(BlockPos pos) { + return placerFunction.apply(pos); + } + + /*public abstract CompoundTag toNBT(CompoundTag root) { + + }*/ +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFSphere.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFSphere.java new file mode 100644 index 00000000..08d2731d --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFSphere.java @@ -0,0 +1,17 @@ +package org.betterx.bclib.sdf.primitive; + +import org.betterx.bclib.util.MHelper; + +public class SDFSphere extends SDFPrimitive { + private float radius; + + public SDFSphere setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return MHelper.length(x, y, z) - radius; + } +} diff --git a/src/main/java/org/betterx/bclib/sdf/primitive/SDFTorus.java b/src/main/java/org/betterx/bclib/sdf/primitive/SDFTorus.java new file mode 100644 index 00000000..424dad67 --- /dev/null +++ b/src/main/java/org/betterx/bclib/sdf/primitive/SDFTorus.java @@ -0,0 +1,24 @@ +package org.betterx.bclib.sdf.primitive; + +import org.betterx.bclib.util.MHelper; + +public class SDFTorus extends SDFPrimitive { + private float radiusSmall; + private float radiusBig; + + public SDFTorus setBigRadius(float radius) { + this.radiusBig = radius; + return this; + } + + public SDFTorus setSmallRadius(float radius) { + this.radiusSmall = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float nx = MHelper.length(x, z) - radiusBig; + return MHelper.length(nx, y) - radiusSmall; + } +} diff --git a/src/main/java/org/betterx/bclib/server/BCLibServer.java b/src/main/java/org/betterx/bclib/server/BCLibServer.java new file mode 100644 index 00000000..e194fa0f --- /dev/null +++ b/src/main/java/org/betterx/bclib/server/BCLibServer.java @@ -0,0 +1,17 @@ +package org.betterx.bclib.server; + +import net.fabricmc.api.DedicatedServerModInitializer; + +import org.betterx.bclib.api.ModIntegrationAPI; +import org.betterx.bclib.api.PostInitAPI; +import org.betterx.bclib.api.dataexchange.DataExchangeAPI; + +public class BCLibServer implements DedicatedServerModInitializer { + @Override + public void onInitializeServer() { + ModIntegrationAPI.registerAll(); + DataExchangeAPI.prepareServerside(); + + PostInitAPI.postInit(false); + } +} diff --git a/src/main/java/org/betterx/bclib/util/BackgroundInfo.java b/src/main/java/org/betterx/bclib/util/BackgroundInfo.java new file mode 100644 index 00000000..4e4d2054 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/BackgroundInfo.java @@ -0,0 +1,9 @@ +package org.betterx.bclib.util; + +public class BackgroundInfo { + public static float fogColorRed; + public static float fogColorGreen; + public static float fogColorBlue; + public static float fogDensity = 1; + public static float blindness; +} diff --git a/src/main/java/org/betterx/bclib/util/BlocksHelper.java b/src/main/java/org/betterx/bclib/util/BlocksHelper.java new file mode 100644 index 00000000..e97e2fc8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/BlocksHelper.java @@ -0,0 +1,204 @@ +package org.betterx.bclib.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ClipContext.Fluid; +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.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; + +import com.google.common.collect.Maps; + +import java.util.Map; +import java.util.Random; + +public class BlocksHelper { + private static final Map COLOR_BY_BLOCK = Maps.newHashMap(); + + public static final int FLAG_UPDATE_BLOCK = 1; + public static final int FLAG_SEND_CLIENT_CHANGES = 2; + public static final int FLAG_NO_RERENDER = 4; + public static final int FORSE_RERENDER = 8; + public static final int FLAG_IGNORE_OBSERVERS = 16; + + public static final int SET_SILENT = FLAG_UPDATE_BLOCK | FLAG_IGNORE_OBSERVERS | FLAG_SEND_CLIENT_CHANGES; + public static final int SET_OBSERV = FLAG_UPDATE_BLOCK | FLAG_SEND_CLIENT_CHANGES; + public static final Direction[] HORIZONTAL = makeHorizontal(); + public static final Direction[] DIRECTIONS = Direction.values(); + + private static final ThreadLocal TL_POS = ThreadLocal.withInitial(() -> new MutableBlockPos()); + + protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); + protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); + + public static void addBlockColor(Block block, int color) { + COLOR_BY_BLOCK.put(block, color); + } + + public static int getBlockColor(Block block) { + return COLOR_BY_BLOCK.getOrDefault(block, 0xFF000000); + } + + public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, BlockState state) { + world.setBlock(pos, state, SET_SILENT); + } + + public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, Block block) { + world.setBlock(pos, block.defaultBlockState(), SET_SILENT); + } + + public static void setWithUpdate(LevelAccessor world, BlockPos pos, BlockState state) { + world.setBlock(pos, state, SET_OBSERV); + } + + public static void setWithUpdate(LevelAccessor world, BlockPos pos, Block block) { + world.setBlock(pos, block.defaultBlockState(), SET_OBSERV); + } + + public static int upRay(LevelAccessor world, BlockPos pos, int maxDist) { + int length = 0; + for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.above(j))); j++) { + length++; + } + return length; + } + + public static int downRay(LevelAccessor world, BlockPos pos, int maxDist) { + int length = 0; + for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.below(j))); j++) { + length++; + } + return length; + } + + public static int downRayRep(LevelAccessor world, BlockPos pos, int maxDist) { + final MutableBlockPos POS = TL_POS.get(); + POS.set(pos); + for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) { + POS.setY(POS.getY() - 1); + } + return pos.getY() - POS.getY(); + } + + public static int raycastSqr(LevelAccessor world, BlockPos pos, int dx, int dy, int dz, int maxDist) { + final MutableBlockPos POS = TL_POS.get(); + POS.set(pos); + for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) { + POS.move(dx, dy, dz); + } + return (int) pos.distSqr(POS); + } + + /** + * Rotates {@link BlockState} horizontally. Used in block classes with {@link Direction} {@link Property} in rotate function. + * + * @param state - {@link BlockState} to mirror; + * @param rotation - {@link Rotation}; + * @param facing - Block {@link Direction} {@link Property}. + * @return Rotated {@link BlockState}. + */ + public static BlockState rotateHorizontal(BlockState state, Rotation rotation, Property facing) { + return state.setValue(facing, rotation.rotate(state.getValue(facing))); + } + + /** + * Mirrors {@link BlockState} horizontally. Used in block classes with {@link Direction} {@link Property} in mirror function. + * + * @param state - {@link BlockState} to mirror; + * @param mirror - {@link Mirror}; + * @param facing - Block {@link Direction} {@link Property}. + * @return Mirrored {@link BlockState}. + */ + public static BlockState mirrorHorizontal(BlockState state, Mirror mirror, Property facing) { + return state.rotate(mirror.getRotation(state.getValue(facing))); + } + + /** + * Counts the amount of same block down. + * + * @param world - {@link LevelAccessor} world; + * @param pos - {@link BlockPos} start position; + * @param block - {@link Block} to count. + * @return Integer amount of blocks. + */ + public static int getLengthDown(LevelAccessor world, BlockPos pos, Block block) { + int count = 1; + while (world.getBlockState(pos.below(count)).getBlock() == block) { + count++; + } + return count; + } + + /** + * Creates a new {@link Direction} array with clockwise order: + * NORTH, EAST, SOUTH, WEST + * + * @return Array of {@link Direction}. + */ + public static Direction[] makeHorizontal() { + return new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST}; + } + + /** + * Get any random horizontal {@link Direction}. + * + * @param random - {@link Random}. + * @return {@link Direction}. + */ + public static Direction randomHorizontal(RandomSource random) { + return HORIZONTAL[random.nextInt(4)]; + } + + /** + * Get any random {@link Direction} including vertical and horizontal. + * + * @param random - {@link Random}. + * @return {@link Direction}. + */ + public static Direction randomDirection(RandomSource random) { + return DIRECTIONS[random.nextInt(6)]; + } + + /** + * Check if block is {@link Fluid} or not. + * + * @param state - {@link BlockState} to check. + * @return {@code true} if block is fluid and {@code false} if not. + */ + public static boolean isFluid(BlockState state) { + return !state.getFluidState().isEmpty(); + } + + /** + * Check if block is "invulnerable" like Bedrock. + * + * @param state - {@link BlockState} to check; + * @param world - {@link BlockGetter} world where BlockState exist; + * @param pos - {@link BlockPos} where BlockState is. + * @return {@code true} if block is "invulnerable" and {@code false} if not. + */ + public static boolean isInvulnerable(BlockState state, BlockGetter world, BlockPos pos) { + return state.getDestroySpeed(world, pos) < 0; + } + + /** + * Check if block is "invulnerable" like Bedrock. Unlike safe function will pass world and position parameters as {@code null}. + * + * @param state - {@link BlockState} to check. + * @return {@code true} if block is "invulnerable" and {@code false} if not. + */ + public static boolean isInvulnerableUnsafe(BlockState state) { + try { + return isInvulnerable(state, null, null); + } catch (Exception e) { + return false; + } + } +} diff --git a/src/main/java/org/betterx/bclib/util/CollectionsUtil.java b/src/main/java/org/betterx/bclib/util/CollectionsUtil.java new file mode 100644 index 00000000..f2076877 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/CollectionsUtil.java @@ -0,0 +1,44 @@ +package org.betterx.bclib.util; + +import java.util.*; + +public class CollectionsUtil { + /** + * Will return mutable copy of list. + * + * @param list {@link List} to make mutable. + * @return {@link ArrayList} or original {@link List} if it is mutable. + */ + public static List getMutable(List list) { + if (list instanceof ArrayList) { + return list; + } + return new ArrayList<>(list); + } + + /** + * Will return mutable copy of set. + * + * @param set {@link Set} to make mutable. + * @return {@link HashSet} or original {@link Set} if it is mutable. + */ + public static Set getMutable(Set set) { + if (set instanceof HashSet) { + return set; + } + return new HashSet<>(set); + } + + /** + * Will return mutable copy of map. + * + * @param map {@link Map} to make mutable. + * @return {@link HashMap} or original {@link Map} if it is mutable. + */ + public static Map getMutable(Map map) { + if (map instanceof HashMap) { + return map; + } + return new HashMap<>(map); + } +} diff --git a/src/main/java/org/betterx/bclib/util/ColorExtractor.java b/src/main/java/org/betterx/bclib/util/ColorExtractor.java new file mode 100644 index 00000000..aa73d7bb --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/ColorExtractor.java @@ -0,0 +1,148 @@ +package org.betterx.bclib.util; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Random; + +public class ColorExtractor { + private final List

centers = new ArrayList<>(); + private final List colors; + private Integer result; + + public ColorExtractor(List colors) { + this.colors = colors; + Random rnd = new Random(); + int size = colors.size(); + for (int i = 0; i < 4; i++) { + int color = colors.get(rnd.nextInt(size)); + this.centers.add(new Center(color)); + } + } + + public int analize() { + boolean moved = true; + while (moved) { + this.remap(); + moved = false; + for (Center center : centers) { + if (center.move()) { + moved = true; + } + } + } + List
toClear = new ArrayList<>(); + this.centers.forEach(center -> { + if (center.colors.isEmpty()) { + toClear.add(center); + } + }); + if (toClear.size() > 0) { + toClear.forEach(clear -> centers.remove(clear)); + } + this.centers.sort(Center.COMPARATOR); + + return this.getResult(); + } + + public int getResult() { + if (result == null) { + double weights = 0; + double alpha = 0; + double red = 0; + double green = 0; + double blue = 0; + for (Center center : centers) { + double weight = (double) center.colors.size() / colors.size(); + weights += weight; + alpha += center.a * weight; + red += center.r * weight; + green += center.g * weight; + blue += center.b * weight; + } + + int a = (int) Math.round(alpha / weights); + int r = (int) Math.round(red / weights); + int g = (int) Math.round(green / weights); + int b = (int) Math.round(blue / weights); + + this.result = a << 24 | r << 16 | g << 8 | b; + } + + return this.result; + } + + private void remap() { + this.centers.forEach(entry -> entry.colors.clear()); + this.colors.forEach(color -> { + int id = 0; + int base = centers.get(0).getColor(); + int dst = ColorUtil.colorDistance(color, base); + for (Center center : centers) { + base = center.getColor(); + int dst1 = ColorUtil.colorDistance(color, base); + if (dst1 < dst) { + dst = dst1; + id = centers.indexOf(center); + } + } + this.centers.get(id).colors.add(color); + }); + } + + private static class Center { + static final Comparator
COMPARATOR = new Comparator
() { + @Override + public int compare(Center c1, Center c2) { + return Integer.compare(c1.getColor(), c2.getColor()); + } + }; + + List colors = new ArrayList<>(); + double a, r, g, b; + + Center(int color) { + this.a = (color >> 24) & 255; + this.r = (color >> 16) & 255; + this.g = (color >> 8) & 255; + this.b = color & 255; + } + + private void update(double a, double r, double g, double b) { + this.a = a; + this.r = r; + this.g = g; + this.b = b; + } + + public int getColor() { + int a = (int) Math.round(this.a); + int r = (int) Math.round(this.r); + int g = (int) Math.round(this.g); + int b = (int) Math.round(this.b); + return a << 24 | r << 16 | g << 8 | b; + } + + public boolean move() { + double or = r; + double og = g; + double ob = b; + double a = 0, r = 0, g = 0, b = 0; + int size = this.colors.size(); + for (int col : colors) { + a += (col >> 24) & 255; + r += (col >> 16) & 255; + g += (col >> 8) & 255; + b += col & 255; + } + a /= size; + r /= size; + g /= size; + b /= size; + + this.update(a, r, g, b); + + return Math.abs(r - or) > 0.1 || Math.abs(g - og) > 0.1 || Math.abs(b - ob) > 0.1; + } + } +} diff --git a/src/main/java/org/betterx/bclib/util/ColorUtil.java b/src/main/java/org/betterx/bclib/util/ColorUtil.java new file mode 100644 index 00000000..f9764a70 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/ColorUtil.java @@ -0,0 +1,244 @@ +package org.betterx.bclib.util; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.Mth; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; + +import com.google.common.collect.Maps; +import com.mojang.blaze3d.platform.NativeImage; +import org.betterx.bclib.BCLib; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ColorUtil { + private static final float[] FLOAT_BUFFER = new float[4]; + private static final int ALPHA = 255 << 24; + + public static int color(int r, int g, int b) { + return ALPHA | (r << 16) | (g << 8) | b; + } + + public static int color(String hex) { + int r = Integer.parseInt(hex.substring(0, 2), 16); + int g = Integer.parseInt(hex.substring(2, 4), 16); + int b = Integer.parseInt(hex.substring(4, 6), 16); + return color(r, g, b); + } + + public static int[] toIntArray(int color) { + return new int[]{(color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, color & 255}; + } + + public static float[] toFloatArray(int color) { + FLOAT_BUFFER[0] = ((color >> 16 & 255) / 255.0F); + FLOAT_BUFFER[1] = ((color >> 8 & 255) / 255.0F); + FLOAT_BUFFER[2] = ((color & 255) / 255.0F); + FLOAT_BUFFER[3] = ((color >> 24 & 255) / 255.0F); + + return FLOAT_BUFFER; + } + + public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) { + float hue, saturation, brightness; + if (hsbvals == null) { + hsbvals = FLOAT_BUFFER; + } + int cmax = (r > g) ? r : g; + if (b > cmax) cmax = b; + int cmin = (r < g) ? r : g; + if (b < cmin) cmin = b; + + brightness = ((float) cmax) / 255.0F; + if (cmax != 0) saturation = ((float) (cmax - cmin)) / ((float) cmax); + else saturation = 0; + if (saturation == 0) hue = 0; + else { + float redc = ((float) (cmax - r)) / ((float) (cmax - cmin)); + float greenc = ((float) (cmax - g)) / ((float) (cmax - cmin)); + float bluec = ((float) (cmax - b)) / ((float) (cmax - cmin)); + if (r == cmax) hue = bluec - greenc; + else if (g == cmax) hue = 2.0F + redc - bluec; + else hue = 4.0F + greenc - redc; + hue = hue / 6.0F; + if (hue < 0) hue = hue + 1.0F; + } + hsbvals[0] = hue; + hsbvals[1] = saturation; + hsbvals[2] = brightness; + return hsbvals; + } + + public static int HSBtoRGB(float hue, float saturation, float brightness) { + int r = 0, g = 0, b = 0; + if (saturation == 0) { + r = g = b = (int) (brightness * 255.0F + 0.5F); + } else { + float h = (hue - (float) Math.floor(hue)) * 6.0F; + float f = h - (float) java.lang.Math.floor(h); + float p = brightness * (1.0F - saturation); + float q = brightness * (1.0F - saturation * f); + float t = brightness * (1.0F - (saturation * (1.0F - f))); + switch ((int) h) { + case 0: + r = (int) (brightness * 255.0F + 0.5F); + g = (int) (t * 255.0F + 0.5F); + b = (int) (p * 255.0F + 0.5F); + break; + case 1: + r = (int) (q * 255.0F + 0.5F); + g = (int) (brightness * 255.0F + 0.5F); + b = (int) (p * 255.0F + 0.5F); + break; + case 2: + r = (int) (p * 255.0F + 0.5F); + g = (int) (brightness * 255.0F + 0.5F); + b = (int) (t * 255.0F + 0.5F); + break; + case 3: + r = (int) (p * 255.0F + 0.5F); + g = (int) (q * 255.0F + 0.5F); + b = (int) (brightness * 255.0F + 0.5F); + break; + case 4: + r = (int) (t * 255.0F + 0.5F); + g = (int) (p * 255.0F + 0.5F); + b = (int) (brightness * 255.0F + 0.5F); + break; + case 5: + r = (int) (brightness * 255.0F + 0.5F); + g = (int) (p * 255.0F + 0.5F); + b = (int) (q * 255.0F + 0.5F); + break; + } + } + return 0xFF000000 | (r << 16) | (g << 8) | (b << 0); + } + + public static int parseHex(String hexColor) { + int len = hexColor.length(); + if (len < 6 || len > 8 || len % 2 > 0) { + return -1; + } + + int color, shift; + if (len == 6) { + color = 0xFF000000; + shift = 16; + } else { + color = 0; + shift = 24; + } + + try { + String[] splited = hexColor.split("(?<=\\G.{2})"); + for (String digit : splited) { + color |= Integer.valueOf(digit, 16) << shift; + shift -= 8; + } + } catch (NumberFormatException ex) { + BCLib.LOGGER.catching(ex); + return -1; + } + + return color; + } + + public static int toABGR(int color) { + int r = (color >> 16) & 255; + int g = (color >> 8) & 255; + int b = color & 255; + return 0xFF000000 | b << 16 | g << 8 | r; + } + + public static int ABGRtoARGB(int color) { + int a = (color >> 24) & 255; + int b = (color >> 16) & 255; + int g = (color >> 8) & 255; + int r = color & 255; + return a << 24 | r << 16 | g << 8 | b; + } + + public static int colorBrigtness(int color, float val) { + RGBtoHSB((color >> 16) & 255, (color >> 8) & 255, color & 255, FLOAT_BUFFER); + FLOAT_BUFFER[2] += val / 10.0F; + FLOAT_BUFFER[2] = Mth.clamp(FLOAT_BUFFER[2], 0.0F, 1.0F); + return HSBtoRGB(FLOAT_BUFFER[0], FLOAT_BUFFER[1], FLOAT_BUFFER[2]); + } + + public static int applyTint(int color, int tint) { + return colorBrigtness(ColorHelper.multiplyColor(color, tint), 1.5F); + } + + public static int colorDistance(int color1, int color2) { + int r1 = (color1 >> 16) & 255; + int g1 = (color1 >> 8) & 255; + int b1 = color1 & 255; + int r2 = (color2 >> 16) & 255; + int g2 = (color2 >> 8) & 255; + int b2 = color2 & 255; + return MHelper.sqr(r1 - r2) + MHelper.sqr(g1 - g2) + MHelper.sqr(b1 - b2); + } + + private static final Map colorPalette = Maps.newHashMap(); + + @Environment(EnvType.CLIENT) + public static int extractColor(Item item) { + ResourceLocation id = Registry.ITEM.getKey(item); + if (id.equals(Registry.ITEM.getDefaultKey())) return -1; + if (colorPalette.containsKey(id)) { + return colorPalette.get(id); + } + ResourceLocation texture; + if (item instanceof BlockItem) { + texture = new ResourceLocation(id.getNamespace(), "textures/block/" + id.getPath() + ".png"); + } else { + texture = new ResourceLocation(id.getNamespace(), "textures/item/" + id.getPath() + ".png"); + } + NativeImage image = loadImage(texture, 16, 16); + List colors = new ArrayList<>(); + for (int i = 0; i < image.getWidth(); i++) { + for (int j = 0; j < 16; j++) { + int col = image.getPixelRGBA(i, j); + if (((col >> 24) & 255) > 0) { + colors.add(ABGRtoARGB(col)); + } + } + } + image.close(); + + if (colors.size() == 0) return -1; + + ColorExtractor extractor = new ColorExtractor(colors); + int color = extractor.analize(); + colorPalette.put(id, color); + + return color; + } + + @Environment(EnvType.CLIENT) + public static NativeImage loadImage(ResourceLocation image, int w, int h) { + Minecraft minecraft = Minecraft.getInstance(); + ResourceManager resourceManager = minecraft.getResourceManager(); + var imgResource = resourceManager.getResource(image); + if (imgResource.isPresent()) { + try { + return NativeImage.read(imgResource.get().open()); + } catch (IOException e) { + BCLib.LOGGER.warning("Can't load texture image: {}. Will be created empty image.", image); + BCLib.LOGGER.warning("Cause: {}.", e.getMessage()); + } + } + return new NativeImage(w, h, false); + } +} \ No newline at end of file diff --git a/src/main/java/org/betterx/bclib/util/ItemUtil.java b/src/main/java/org/betterx/bclib/util/ItemUtil.java new file mode 100644 index 00000000..3a920a80 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/ItemUtil.java @@ -0,0 +1,73 @@ +package org.betterx.bclib.util; + +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 com.google.gson.JsonObject; +import org.betterx.bclib.BCLib; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +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; + } +} diff --git a/src/main/java/org/betterx/bclib/util/JsonFactory.java b/src/main/java/org/betterx/bclib/util/JsonFactory.java new file mode 100644 index 00000000..fd908d43 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/JsonFactory.java @@ -0,0 +1,131 @@ +package org.betterx.bclib.util; + +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.betterx.bclib.BCLib; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import org.jetbrains.annotations.Nullable; + +public class JsonFactory { + public final static Gson GSON = new GsonBuilder().setPrettyPrinting() + .create(); + + public static JsonObject getJsonObject(InputStream stream) { + try { + Reader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); + JsonElement json = loadJson(reader); + if (json != null && json.isJsonObject()) { + JsonObject jsonObject = json.getAsJsonObject(); + return jsonObject != null ? jsonObject : new JsonObject(); + } + } catch (Exception ex) { + BCLib.LOGGER.catching(ex); + } + return new JsonObject(); + } + + public static JsonObject getJsonObject(File jsonFile) { + if (jsonFile.exists()) { + JsonElement json = loadJson(jsonFile); + if (json != null && json.isJsonObject()) { + JsonObject jsonObject = json.getAsJsonObject(); + return jsonObject != null ? jsonObject : new JsonObject(); + } + } + return new JsonObject(); + } + + /** + * Loads {@link JsonObject} from resource location using Minecraft resource manager. Can be used to load JSON from resourcepacks and resources. + * + * @param location {@link ResourceLocation} to JSON file + * @return {@link JsonObject} + */ + @Nullable + @Environment(EnvType.CLIENT) + public static JsonObject getJsonObject(ResourceLocation location) { + ResourceManager manager = Minecraft.getInstance() + .getResourceManager(); + JsonObject obj = null; + try { + Resource resource = manager.getResource(location).orElse(null); + if (resource != null) { + InputStream stream = resource.open(); + InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); + obj = JsonFactory.GSON.fromJson(reader, JsonObject.class); + reader.close(); + stream.close(); + } + } catch (IOException ex) { + } + return obj; + } + + public static JsonElement loadJson(File jsonFile) { + if (jsonFile.exists()) { + try (Reader reader = new FileReader(jsonFile, StandardCharsets.UTF_8)) { + return loadJson(reader); + } catch (Exception ex) { + BCLib.LOGGER.catching(ex); + } + } + return null; + } + + public static JsonElement loadJson(Reader reader) { + return GSON.fromJson(reader, JsonElement.class); + } + + public static void storeJson(File jsonFile, JsonElement jsonObject) { + try (FileWriter writer = new FileWriter(jsonFile, StandardCharsets.UTF_8)) { + String json = GSON.toJson(jsonObject); + writer.write(json); + writer.flush(); + } catch (IOException ex) { + BCLib.LOGGER.catching(ex); + } + } + + public static void storeJson(OutputStream outStream, JsonElement jsonObject) { + OutputStreamWriter writer = new OutputStreamWriter(outStream, StandardCharsets.UTF_8); + GSON.toJson(jsonObject, writer); + try { + writer.flush(); + } catch (IOException e) { + BCLib.LOGGER.error(e.getMessage()); + e.printStackTrace(); + } + } + + public static int getInt(JsonObject object, String member, int def) { + JsonElement elem = object.get(member); + return elem == null ? def : elem.getAsInt(); + } + + public static float getFloat(JsonObject object, String member, float def) { + JsonElement elem = object.get(member); + return elem == null ? def : elem.getAsFloat(); + } + + public static boolean getBoolean(JsonObject object, String member, boolean def) { + JsonElement elem = object.get(member); + return elem == null ? def : elem.getAsBoolean(); + } + + public static String getString(JsonObject object, String member, String def) { + JsonElement elem = object.get(member); + return elem == null ? def : elem.getAsString(); + } +} diff --git a/src/main/java/org/betterx/bclib/util/Logger.java b/src/main/java/org/betterx/bclib/util/Logger.java new file mode 100644 index 00000000..373bf2a6 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/Logger.java @@ -0,0 +1,62 @@ +package org.betterx.bclib.util; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; + +public final class Logger { + private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(); + private final String modPref; + + public Logger(String modID) { + this.modPref = "[" + modID + "] "; + } + + public void log(Level level, String message) { + LOGGER.log(level, modPref + message); + } + + public void log(Level level, String message, Object... params) { + LOGGER.log(level, modPref + message, params); + } + + public void debug(Object message) { + this.log(Level.DEBUG, message.toString()); + } + + public void debug(Object message, Object... params) { + this.log(Level.DEBUG, message.toString(), params); + } + + public void catching(Throwable ex) { + this.error(ex.getLocalizedMessage()); + LOGGER.catching(ex); + } + + public void info(String message) { + this.log(Level.INFO, message); + } + + public void info(String message, Object... params) { + this.log(Level.INFO, message, params); + } + + public void warning(String message, Object... params) { + this.log(Level.WARN, message, params); + } + + public void warning(String message, Object obj, Exception ex) { + LOGGER.warn(modPref + message, obj, ex); + } + + public void error(String message) { + this.log(Level.ERROR, message); + } + + public void error(String message, Object obj, Exception ex) { + LOGGER.error(modPref + message, obj, ex); + } + + public void error(String message, Exception ex) { + LOGGER.error(modPref + message, ex); + } +} diff --git a/src/main/java/ru/bclib/util/LootUtil.java b/src/main/java/org/betterx/bclib/util/LootUtil.java similarity index 80% rename from src/main/java/ru/bclib/util/LootUtil.java rename to src/main/java/org/betterx/bclib/util/LootUtil.java index 05cf3cb9..04ea0634 100644 --- a/src/main/java/ru/bclib/util/LootUtil.java +++ b/src/main/java/org/betterx/bclib/util/LootUtil.java @@ -1,4 +1,4 @@ -package ru.bclib.util; +package org.betterx.bclib.util; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; @@ -15,13 +15,16 @@ import java.util.List; import java.util.Optional; public class LootUtil { - public static Optional> getDrops(BlockBehaviour block, BlockState state, LootContext.Builder builder){ + public static Optional> getDrops(BlockBehaviour block, + BlockState state, + LootContext.Builder builder) { ResourceLocation tableID = block.getLootTable(); if (tableID == BuiltInLootTables.EMPTY) { return Optional.empty(); } - final LootContext ctx = builder.withParameter(LootContextParams.BLOCK_STATE, state).create(LootContextParamSets.BLOCK); + final LootContext ctx = builder.withParameter(LootContextParams.BLOCK_STATE, state) + .create(LootContextParamSets.BLOCK); final ServerLevel level = ctx.getLevel(); final LootTable table = level.getServer().getLootTables().get(tableID); diff --git a/src/main/java/org/betterx/bclib/util/MHelper.java b/src/main/java/org/betterx/bclib/util/MHelper.java new file mode 100644 index 00000000..a6702506 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/MHelper.java @@ -0,0 +1,226 @@ +package org.betterx.bclib.util; + +import net.minecraft.core.Vec3i; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.LegacyRandomSource; + +import com.mojang.math.Vector3f; + +import java.util.Random; + +public class MHelper { + private static final Vec3i[] RANDOM_OFFSETS = new Vec3i[3 * 3 * 3 - 1]; + private static final float RAD_TO_DEG = 57.295779513082320876798154814105F; + public static final float PHI = (float) (Math.PI * (3 - Math.sqrt(5))); + public static final float PI2 = (float) (Math.PI * 2); + public static final Random RANDOM = new Random(); + public static final RandomSource RANDOM_SOURCE = new LegacyRandomSource(RANDOM.nextLong()); + + public static int randRange(int min, int max, RandomSource random) { + return min + random.nextInt(max - min + 1); + } + + public static double randRange(double min, double max, RandomSource random) { + return min + random.nextDouble() * (max - min); + } + + public static float randRange(float min, float max, RandomSource random) { + return min + random.nextFloat() * (max - min); + } + + public static byte setBit(byte source, int pos, boolean value) { + return value ? setBitTrue(source, pos) : setBitFalse(source, pos); + } + + public static byte setBitTrue(byte source, int pos) { + source |= 1 << pos; + return source; + } + + public static byte setBitFalse(byte source, int pos) { + source &= ~(1 << pos); + return source; + } + + public static boolean getBit(byte source, int pos) { + return ((source >> pos) & 1) == 1; + } + + public static float wrap(float x, float side) { + return x - floor(x / side) * side; + } + + public static int floor(double x) { + return x < 0 ? (int) (x - 1) : (int) x; + } + + public static int min(int a, int b) { + return a < b ? a : b; + } + + public static int min(int a, int b, int c) { + return min(a, min(b, c)); + } + + public static int max(int a, int b) { + return a > b ? a : b; + } + + public static float min(float a, float b) { + return a < b ? a : b; + } + + public static float max(float a, float b) { + return a > b ? a : b; + } + + public static float max(float a, float b, float c) { + return max(a, max(b, c)); + } + + public static int max(int a, int b, int c) { + return max(a, max(b, c)); + } + + public static boolean isEven(int num) { + return (num & 1) == 0; + } + + public static float lengthSqr(float x, float y, float z) { + return x * x + y * y + z * z; + } + + public static double lengthSqr(double x, double y, double z) { + return x * x + y * y + z * z; + } + + public static float length(float x, float y, float z) { + return (float) Math.sqrt(lengthSqr(x, y, z)); + } + + public static double length(double x, double y, double z) { + return Math.sqrt(lengthSqr(x, y, z)); + } + + public static float lengthSqr(float x, float y) { + return x * x + y * y; + } + + public static double lengthSqr(double x, double y) { + return x * x + y * y; + } + + public static float length(float x, float y) { + return (float) Math.sqrt(lengthSqr(x, y)); + } + + public static double length(double x, double y) { + return Math.sqrt(lengthSqr(x, y)); + } + + public static float dot(float x1, float y1, float z1, float x2, float y2, float z2) { + return x1 * x2 + y1 * y2 + z1 * z2; + } + + public static float dot(float x1, float y1, float x2, float y2) { + return x1 * x2 + y1 * y2; + } + + public static int getRandom(int x, int z) { + int h = x * 374761393 + z * 668265263; + h = (h ^ (h >> 13)) * 1274126177; + return h ^ (h >> 16); + } + + public static int getSeed(int seed, int x, int y) { + int h = seed + x * 374761393 + y * 668265263; + h = (h ^ (h >> 13)) * 1274126177; + return h ^ (h >> 16); + } + + public static int getSeed(int seed, int x, int y, int z) { + int h = seed + x * 374761393 + y * 668265263 + z; + h = (h ^ (h >> 13)) * 1274126177; + return h ^ (h >> 16); + } + + public static void shuffle(T[] array, RandomSource random) { + for (int i = 0; i < array.length; i++) { + int i2 = random.nextInt(array.length); + T element = array[i]; + array[i] = array[i2]; + array[i2] = element; + } + } + + public static int sqr(int i) { + return i * i; + } + + public static float sqr(float f) { + return f * f; + } + + public static double sqr(double d) { + return d * d; + } + + public static final float radiansToDegrees(float value) { + return value * RAD_TO_DEG; + } + + public static final float degreesToRadians(float value) { + return value / RAD_TO_DEG; + } + + public static Vector3f cross(Vector3f vec1, Vector3f vec2) { + float cx = vec1.y() * vec2.z() - vec1.z() * vec2.y(); + float cy = vec1.z() * vec2.x() - vec1.x() * vec2.z(); + float cz = vec1.x() * vec2.y() - vec1.y() * vec2.x(); + return new Vector3f(cx, cy, cz); + } + + public static Vector3f normalize(Vector3f vec) { + float length = lengthSqr(vec.x(), vec.y(), vec.z()); + if (length > 0) { + length = (float) Math.sqrt(length); + float x = vec.x() / length; + float y = vec.y() / length; + float z = vec.z() / length; + vec.set(x, y, z); + } + return vec; + } + + public static float angle(Vector3f vec1, Vector3f vec2) { + float dot = vec1.x() * vec2.x() + vec1.y() * vec2.y() + vec1.z() * vec2.z(); + float length1 = lengthSqr(vec1.x(), vec1.y(), vec1.z()); + float length2 = lengthSqr(vec2.x(), vec2.y(), vec2.z()); + return (float) Math.acos(dot / Math.sqrt(length1 * length2)); + } + + public static Vector3f randomHorizontal(RandomSource random) { + float angleY = randRange(0, PI2, random); + float vx = (float) Math.sin(angleY); + float vz = (float) Math.cos(angleY); + return new Vector3f(vx, 0, vz); + } + + public static Vec3i[] getOffsets(RandomSource random) { + shuffle(RANDOM_OFFSETS, random); + return RANDOM_OFFSETS; + } + + static { + int index = 0; + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + if (x != 0 || y != 0 || z != 0) { + RANDOM_OFFSETS[index++] = new Vec3i(x, y, z); + } + } + } + } + } +} diff --git a/src/main/java/org/betterx/bclib/util/MethodReplace.java b/src/main/java/org/betterx/bclib/util/MethodReplace.java new file mode 100644 index 00000000..5dbe1ca7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/MethodReplace.java @@ -0,0 +1,46 @@ +package org.betterx.bclib.util; + +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase; + +import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + +public class MethodReplace { + private static Function itemReplace; + private static Function blockReplace; + private static Block block; + private static Item item; + + public static void addItemReplace(Item item, Function itemReplace) { + MethodReplace.itemReplace = itemReplace; + MethodReplace.item = item; + } + + public static void addBlockReplace(Block block, Function blockReplace) { + MethodReplace.blockReplace = blockReplace; + MethodReplace.block = block; + } + + @Nullable + public static Function getItemReplace(Item item) { + if (MethodReplace.item != item) { + return null; + } + Function replace = itemReplace; + itemReplace = null; + return replace; + } + + @Nullable + public static Function getBlockReplace(Block block) { + if (MethodReplace.block != block) { + return null; + } + Function replace = blockReplace; + blockReplace = null; + return replace; + } +} diff --git a/src/main/java/org/betterx/bclib/util/ModUtil.java b/src/main/java/org/betterx/bclib/util/ModUtil.java new file mode 100644 index 00000000..e3d4a681 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/ModUtil.java @@ -0,0 +1,434 @@ +package org.betterx.bclib.util; + +import net.fabricmc.loader.api.*; +import net.fabricmc.loader.api.metadata.*; +import net.fabricmc.loader.util.version.SemanticVersionImpl; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import org.apache.logging.log4j.LogManager; +import org.betterx.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.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ModUtil { + private static Map mods; + + /** + * Unloads the cache of available mods created from {@link #getMods()} + */ + public static void invalidateCachedMods() { + mods = null; + } + + /** + * return a map of all mods that were found in the 'mods'-folder. + *

+ * The method will cache the results. You can clear that cache (and free the memory) by + * calling {@link #invalidateCachedMods()} + *

+ * An error message is printed if a mod fails to load, but the parsing will continue. + * + * @return A map of all found mods. (key=ModID, value={@link ModInfo}) + */ + public static Map getMods() { + if (mods != null) return mods; + + mods = new HashMap<>(); + org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("BCLib|ModLoader"); + PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (ModUtil::accept)); + + 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 getProvides() { + return new ArrayList<>(); + } + + @Override + public ModEnvironment getEnvironment() { + JsonElement env = data.get("environment"); + if (env == null) { + BCLib.LOGGER.warning("No environment specified in " + sourceFile); + //return ModEnvironment.UNIVERSAL; + } + final String environment = env == null ? "" : env.getAsString() + .toLowerCase(Locale.ROOT); + + if (environment.isEmpty() || environment.equals("*") || 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 getDepends() { + return new ArrayList<>(); + } + + @Override + public Collection getRecommends() { + return new ArrayList<>(); + } + + @Override + public Collection getSuggests() { + return new ArrayList<>(); + } + + @Override + public Collection getConflicts() { + return new ArrayList<>(); + } + + @Override + public Collection getBreaks() { + return new ArrayList<>(); + } + + public Collection getDependencies() { + return new ArrayList<>(); + } + + @Override + public String getName() { + return data.get("name") + .getAsString(); + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public Collection getAuthors() { + return new ArrayList<>(); + } + + @Override + public Collection getContributors() { + return new ArrayList<>(); + } + + @Override + public ContactInformation getContact() { + return null; + } + + @Override + public Collection getLicense() { + return new ArrayList<>(); + } + + @Override + public Optional 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 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. + *

+ * The call will also return null if the mode-Version in the jar-File is not the same + * as the version of the loaded Mod. + * + * @param modID The mod ID to query + * @return A {@link ModInfo}-Object for the querried Mod. + */ + public static ModInfo getModInfo(String modID) { + return getModInfo(modID, true); + } + + public static ModInfo getModInfo(String modID, boolean matchVersion) { + getMods(); + final ModInfo mi = mods.get(modID); + if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null; + return mi; + } + + /** + * Local Mod Version for the queried Mod + * + * @param modID The mod ID to query + * @return The version of the locally installed Mod + */ + public static String getModVersion(String modID) { + Optional optional = FabricLoader.getInstance() + .getModContainer(modID); + if (optional.isPresent()) { + ModContainer modContainer = optional.get(); + return ModInfo.versionToString(modContainer.getMetadata() + .getVersion()); + + } + + return getModVersionFromJar(modID); + } + + /** + * Local Mod Version for the queried Mod from the Jar-File in the games mod-directory + * + * @param modID The mod ID to query + * @return The version of the locally installed Mod + */ + public static String getModVersionFromJar(String modID) { + final ModInfo mi = getModInfo(modID, false); + if (mi != null) return mi.getVersion(); + + return "0.0.0"; + } + + /** + * Get mod version from string. String should be in format: %d.%d.%d + * + * @param version - {@link String} mod version. + * @return int mod version. + */ + public static int convertModVersion(String version) { + if (version.isEmpty()) { + return 0; + } + try { + int res = 0; + final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*"; + final Matcher matcher = Pattern.compile(semanticVersionPattern) + .matcher(version); + if (matcher.find()) { + if (matcher.groupCount() > 0) + res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22); + if (matcher.groupCount() > 1) + res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14); + if (matcher.groupCount() > 3) + res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF; + } + + return res; + } catch (Exception e) { + return 0; + } + } + + /** + * Get mod version from integer. String will be in format %d.%d.%d + * + * @param version - mod version in integer form. + * @return {@link String} mod version. + */ + public static String convertModVersion(int version) { + int a = (version >> 22) & 0xFF; + int b = (version >> 14) & 0xFF; + int c = version & 0x3FFF; + return String.format(Locale.ROOT, "%d.%d.%d", a, b, c); + } + + /** + * {@code true} if the version v1 is larger than v2 + * + * @param v1 A Version string + * @param v2 Another Version string + * @return v1 > v2 + */ + public static boolean isLargerVersion(String v1, String v2) { + return convertModVersion(v1) > convertModVersion(v2); + } + + /** + * {@code true} if the version v1 is larger or equal v2 + * + * @param v1 A Version string + * @param v2 Another Version string + * @return v1 ≥ v2 + */ + public static boolean isLargerOrEqualVersion(String v1, String v2) { + return convertModVersion(v1) >= convertModVersion(v2); + } + + private static void accept(Path file) { + try { + URI uri = URI.create("jar:" + file.toUri()); + + FileSystem fs; + // boolean doClose = false; + try { + fs = FileSystems.getFileSystem(uri); + } catch (Exception e) { + // doClose = true; + fs = FileSystems.newFileSystem(file); + } + if (fs != null) { + try { + 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()); + ModMetadata mc = readJSON(is, uri.toString()); + if (mc != null) { + mods.put(mc.getId(), new ModInfo(mc, file)); + } + } + } + } catch (Exception e) { + BCLib.LOGGER.error("Error for " + uri + ": " + e); + } + //if (doClose) fs.close(); + } + } catch (Exception e) { + BCLib.LOGGER.error("Error for " + file.toUri() + ": " + e); + e.printStackTrace(); + } + } + + public static class ModInfo { + public final ModMetadata metadata; + public final Path jarPath; + + ModInfo(ModMetadata metadata, Path jarPath) { + this.metadata = metadata; + this.jarPath = jarPath; + } + + public static String versionToString(Version v) { + if (v instanceof SemanticVersion) { + return versionToString((SemanticVersion) v); + } + return convertModVersion(convertModVersion(v.toString())); + } + + public static String versionToString(SemanticVersion v) { + StringBuilder stringBuilder = new StringBuilder(); + boolean first = true; + final int cCount = Math.min(v.getVersionComponentCount(), 3); + for (int i = 0; i < cCount; i++) { + if (first) { + first = false; + } else { + stringBuilder.append('.'); + } + + stringBuilder.append(v.getVersionComponent(i)); + } + + return stringBuilder.toString(); + } + + @Override + public String toString() { + return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}'; + } + + public String getVersion() { + if (metadata == null) { + return "0.0.0"; + } + return versionToString(metadata.getVersion()); + } + } +} diff --git a/src/main/java/org/betterx/bclib/util/Pair.java b/src/main/java/org/betterx/bclib/util/Pair.java new file mode 100644 index 00000000..44c9db97 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/Pair.java @@ -0,0 +1,31 @@ +package org.betterx.bclib.util; + +import java.util.Objects; + +public class Pair { + public final A first; + public final B second; + + public Pair(A first, B second) { + this.first = first; + this.second = second; + } + + @Override + public String toString() { + return "Pair{" + "first=" + first + ", second=" + second + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Pair)) return false; + Pair pair = (Pair) o; + return Objects.equals(first, pair.first) && Objects.equals(second, pair.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } +} diff --git a/src/main/java/org/betterx/bclib/util/PathUtil.java b/src/main/java/org/betterx/bclib/util/PathUtil.java new file mode 100644 index 00000000..c53a6e0a --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/PathUtil.java @@ -0,0 +1,100 @@ +package org.betterx.bclib.util; + +import net.fabricmc.loader.api.FabricLoader; + +import java.io.File; +import java.nio.file.Path; +import java.util.function.Consumer; + +public class PathUtil { + public final static Path GAME_FOLDER = FabricLoader.getInstance() + .getGameDir() + .normalize(); + + public final static Path MOD_FOLDER = FabricLoader.getInstance() + .getGameDir() + .resolve("mods") + .normalize(); + + public final static Path MOD_BAK_FOLDER = MOD_FOLDER.resolve("_bclib_deactivated") + .normalize(); + + /** + * Tests if a path is a child-path. + *

+ * A path is a child of another if it is located in the parent or any of the parents subdirectories + * + * @param parent The folder we search for the {@code child} + * @param child The folder you want to test + * @return {@code true} if {@code child} is in {@code parent} or any of its sub directories + */ + public static boolean isChildOf(Path parent, Path child) { + if (child == null || parent == null) return false; + parent = parent.toAbsolutePath().normalize(); + child = child.toAbsolutePath().normalize(); + + final int pCount = parent.getNameCount(); + final int cCount = child.getNameCount(); + + if (cCount > pCount) return isChildOf(parent, child.getParent()); + if (cCount < pCount) return false; + + return child.equals(parent); + } + + /** + * A simple directory walker that ignores dot-files + * + * @param path The path where you want to start + * @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object + * for each visited file + */ + public static void fileWalker(File path, Consumer pathConsumer) { + fileWalker(path, true, pathConsumer); + } + + /** + * A simple directory walker that ignores dot-files + * + * @param path The path where you want to start + * @param recursive if {@code false}, only the {@code path} is traversed + * @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object + * for each visited file + */ + public static void fileWalker(File path, boolean recursive, Consumer pathConsumer) { + if (!path.exists()) return; + for (final File f : path.listFiles()) { + if (f.getName() + .startsWith(".")) continue; + if (f.isDirectory()) { + if (recursive) fileWalker(f, pathConsumer); + } else if (f.isFile()) { + pathConsumer.accept(f.toPath()); + } + } + } + + /** + * Creates a human readable File-Size + * + * @param size Filesize in bytes + * @return A Human readable String + */ + public static String humanReadableFileSize(long size) { + final int threshold = 2; + final int factor = 1024; + if (size < 0) return "? Byte"; + if (size < factor * threshold) { + return size + " Byte"; + } + char[] units = {'K', 'M', 'G', 'T', 'P'}; + int unitIndex = 0; + double fSize = size; + do { + unitIndex++; + fSize /= 1024; + } while (fSize > factor * threshold && unitIndex < units.length); + + return String.format("%.1f %ciB", fSize, units[unitIndex - 1]); + } +} diff --git a/src/main/java/org/betterx/bclib/util/RecipeHelper.java b/src/main/java/org/betterx/bclib/util/RecipeHelper.java new file mode 100644 index 00000000..340e090b --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/RecipeHelper.java @@ -0,0 +1,24 @@ +package org.betterx.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; + } +} diff --git a/src/main/java/org/betterx/bclib/util/SplineHelper.java b/src/main/java/org/betterx/bclib/util/SplineHelper.java new file mode 100644 index 00000000..a1dbacf3 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/SplineHelper.java @@ -0,0 +1,370 @@ +package org.betterx.bclib.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.state.BlockState; + +import com.google.common.collect.Lists; +import com.mojang.math.Vector3f; +import org.betterx.bclib.sdf.SDF; +import org.betterx.bclib.sdf.operator.SDFUnion; +import org.betterx.bclib.sdf.primitive.SDFLine; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +public class SplineHelper { + public static List makeSpline(float x1, float y1, float z1, float x2, float y2, float z2, int points) { + List spline = Lists.newArrayList(); + spline.add(new Vector3f(x1, y1, z1)); + int count = points - 1; + for (int i = 1; i < count; i++) { + float delta = (float) i / (float) count; + float x = Mth.lerp(delta, x1, x2); + float y = Mth.lerp(delta, y1, y2); + float z = Mth.lerp(delta, z1, z2); + spline.add(new Vector3f(x, y, z)); + } + spline.add(new Vector3f(x2, y2, z2)); + return spline; + } + + public static List smoothSpline(List spline, int segmentPoints) { + List result = Lists.newArrayList(); + Vector3f start = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f end = spline.get(i); + for (int j = 0; j < segmentPoints; j++) { + float delta = (float) j / segmentPoints; + delta = 0.5F - 0.5F * Mth.cos(delta * 3.14159F); + result.add(lerp(start, end, delta)); + } + start = end; + } + result.add(start); + return result; + } + + private static Vector3f lerp(Vector3f start, Vector3f end, float delta) { + float x = Mth.lerp(delta, start.x(), end.x()); + float y = Mth.lerp(delta, start.y(), end.y()); + float z = Mth.lerp(delta, start.z(), end.z()); + return new Vector3f(x, y, z); + } + + public static void offsetParts(List spline, RandomSource random, float dx, float dy, float dz) { + int count = spline.size(); + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float x = pos.x() + (float) random.nextGaussian() * dx; + float y = pos.y() + (float) random.nextGaussian() * dy; + float z = pos.z() + (float) random.nextGaussian() * dz; + pos.set(x, y, z); + } + } + + public static void powerOffset(List spline, float distance, float power) { + int count = spline.size(); + float max = count + 1; + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float x = (float) i / max; + float y = pos.y() + (float) Math.pow(x, power) * distance; + pos.set(pos.x(), y, pos.z()); + } + } + + public static SDF buildSDF(List spline, + float radius1, + float radius2, + Function placerFunction) { + int count = spline.size(); + float max = count - 2; + SDF result = null; + Vector3f start = spline.get(0); + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float delta = (float) (i - 1) / max; + SDF line = new SDFLine().setRadius(Mth.lerp(delta, radius1, radius2)) + .setStart(start.x(), start.y(), start.z()) + .setEnd(pos.x(), pos.y(), pos.z()) + .setBlock(placerFunction); + result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line); + start = pos; + } + return result; + } + + public static SDF buildSDF(List spline, + Function radiusFunction, + Function placerFunction) { + int count = spline.size(); + float max = count - 2; + SDF result = null; + Vector3f start = spline.get(0); + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float delta = (float) (i - 1) / max; + SDF line = new SDFLine().setRadius(radiusFunction.apply(delta)) + .setStart(start.x(), start.y(), start.z()) + .setEnd(pos.x(), pos.y(), pos.z()) + .setBlock(placerFunction); + result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line); + start = pos; + } + return result; + } + + public static boolean fillSpline(List spline, + WorldGenLevel world, + BlockState state, + BlockPos pos, + Function replace) { + Vector3f startPos = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f endPos = spline.get(i); + if (!(fillLine(startPos, endPos, world, state, pos, replace))) { + return false; + } + startPos = endPos; + } + + return true; + } + + public static void fillSplineForce(List spline, + WorldGenLevel world, + BlockState state, + BlockPos pos, + Function replace) { + Vector3f startPos = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f endPos = spline.get(i); + fillLineForce(startPos, endPos, world, state, pos, replace); + startPos = endPos; + } + } + + public static boolean fillLine(Vector3f start, + Vector3f end, + WorldGenLevel world, + BlockState state, + BlockPos pos, + Function replace) { + float dx = end.x() - start.x(); + float dy = end.y() - start.y(); + float dz = end.z() - start.z(); + float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); + int count = MHelper.floor(max + 1); + dx /= max; + dy /= max; + dz /= max; + float x = start.x(); + float y = start.y(); + float z = start.z(); + boolean down = Math.abs(dy) > 0.2; + + BlockState bState; + MutableBlockPos bPos = new MutableBlockPos(); + for (int i = 0; i < count; i++) { + bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); + bState = world.getBlockState(bPos); + if (bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } else { + return false; + } + x += dx; + y += dy; + z += dz; + } + bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ()); + bState = world.getBlockState(bPos); + if (bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + return true; + } else { + return false; + } + } + + public static void fillLineForce(Vector3f start, + Vector3f end, + WorldGenLevel world, + BlockState state, + BlockPos pos, + Function replace) { + float dx = end.x() - start.x(); + float dy = end.y() - start.y(); + float dz = end.z() - start.z(); + float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); + int count = MHelper.floor(max + 1); + dx /= max; + dy /= max; + dz /= max; + float x = start.x(); + float y = start.y(); + float z = start.z(); + boolean down = Math.abs(dy) > 0.2; + + BlockState bState; + MutableBlockPos bPos = new MutableBlockPos(); + for (int i = 0; i < count; i++) { + bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); + bState = world.getBlockState(bPos); + if (replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + x += dx; + y += dy; + z += dz; + } + bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ()); + bState = world.getBlockState(bPos); + if (replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + } + + public static boolean canGenerate(List spline, + float scale, + BlockPos start, + WorldGenLevel world, + Function canReplace) { + int count = spline.size(); + Vector3f vec = spline.get(0); + MutableBlockPos mut = new MutableBlockPos(); + float x1 = start.getX() + vec.x() * scale; + float y1 = start.getY() + vec.y() * scale; + float z1 = start.getZ() + vec.z() * scale; + for (int i = 1; i < count; i++) { + vec = spline.get(i); + float x2 = start.getX() + vec.x() * scale; + float y2 = start.getY() + vec.y() * scale; + float z2 = start.getZ() + vec.z() * scale; + + for (float py = y1; py < y2; py += 3) { + if (py - start.getY() < 10) continue; + float lerp = (py - y1) / (y2 - y1); + float x = Mth.lerp(lerp, x1, x2); + float z = Mth.lerp(lerp, z1, z2); + mut.set(x, py, z); + if (!canReplace.apply(world.getBlockState(mut))) { + return false; + } + } + + x1 = x2; + y1 = y2; + z1 = z2; + } + return true; + } + + public static boolean canGenerate(List spline, + BlockPos start, + WorldGenLevel world, + Function canReplace) { + int count = spline.size(); + Vector3f vec = spline.get(0); + MutableBlockPos mut = new MutableBlockPos(); + float x1 = start.getX() + vec.x(); + float y1 = start.getY() + vec.y(); + float z1 = start.getZ() + vec.z(); + for (int i = 1; i < count; i++) { + vec = spline.get(i); + float x2 = start.getX() + vec.x(); + float y2 = start.getY() + vec.y(); + float z2 = start.getZ() + vec.z(); + + for (float py = y1; py < y2; py += 3) { + if (py - start.getY() < 10) continue; + float lerp = (py - y1) / (y2 - y1); + float x = Mth.lerp(lerp, x1, x2); + float z = Mth.lerp(lerp, z1, z2); + mut.set(x, py, z); + if (!canReplace.apply(world.getBlockState(mut))) { + return false; + } + } + + x1 = x2; + y1 = y2; + z1 = z2; + } + return true; + } + + public static Vector3f getPos(List spline, float index) { + int i = (int) index; + int last = spline.size() - 1; + if (i >= last) { + return spline.get(last); + } + float delta = index - i; + Vector3f p1 = spline.get(i); + Vector3f p2 = spline.get(i + 1); + float x = Mth.lerp(delta, p1.x(), p2.x()); + float y = Mth.lerp(delta, p1.y(), p2.y()); + float z = Mth.lerp(delta, p1.z(), p2.z()); + return new Vector3f(x, y, z); + } + + public static void rotateSpline(List spline, float angle) { + for (Vector3f v : spline) { + float sin = (float) Math.sin(angle); + float cos = (float) Math.cos(angle); + float x = v.x() * cos + v.z() * sin; + float z = v.x() * sin + v.z() * cos; + v.set(x, v.y(), z); + } + } + + public static List copySpline(List spline) { + List result = new ArrayList(spline.size()); + for (Vector3f v : spline) { + result.add(new Vector3f(v.x(), v.y(), v.z())); + } + return result; + } + + public static void scale(List spline, float scale) { + scale(spline, scale, scale, scale); + } + + public static void scale(List spline, float x, float y, float z) { + for (Vector3f v : spline) { + v.set(v.x() * x, v.y() * y, v.z() * z); + } + } + + public static void offset(List spline, Vector3f offset) { + for (Vector3f v : spline) { + v.set(offset.x() + v.x(), offset.y() + v.y(), offset.z() + v.z()); + } + } +} diff --git a/src/main/java/org/betterx/bclib/util/StructureHelper.java b/src/main/java/org/betterx/bclib/util/StructureHelper.java new file mode 100644 index 00000000..96ba28ec --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/StructureHelper.java @@ -0,0 +1,137 @@ +package org.betterx.bclib.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import net.minecraft.world.phys.Vec3; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class StructureHelper { + public static StructureTemplate readStructure(ResourceLocation resource) { + String ns = resource.getNamespace(); + String nm = resource.getPath(); + return readStructure("/data/" + ns + "/structures/" + nm + ".nbt"); + } + + public static StructureTemplate readStructure(File datapack, String path) { + if (datapack.isDirectory()) { + return readStructure(datapack + "/" + path); + } else if (datapack.isFile() && datapack.getName().endsWith(".zip")) { + try { + ZipFile zipFile = new ZipFile(datapack); + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String name = entry.getName(); + long compressedSize = entry.getCompressedSize(); + long normalSize = entry.getSize(); + String type = entry.isDirectory() ? "DIR" : "FILE"; + + System.out.println(name); + System.out.format("\t %s - %d - %d\n", type, compressedSize, normalSize); + } + zipFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + public static StructureTemplate readStructure(String path) { + try { + InputStream inputstream = StructureHelper.class.getResourceAsStream(path); + return readStructureFromStream(inputstream); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException { + CompoundTag nbttagcompound = NbtIo.readCompressed(stream); + + StructureTemplate template = new StructureTemplate(); + template.load(nbttagcompound); + + return template; + } + + public static BlockPos offsetPos(BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror) { + Vec3 offset = StructureTemplate.transform( + Vec3.atCenterOf(structure.getSize()), + mirror, + rotation, + BlockPos.ZERO + ); + return pos.offset(-offset.x * 0.5, 0, -offset.z * 0.5); + } + + public static void placeCenteredBottom(WorldGenLevel world, + BlockPos pos, + StructureTemplate structure, + Rotation rotation, + Mirror mirror, + RandomSource random) { + placeCenteredBottom(world, pos, structure, rotation, mirror, makeBox(pos), random); + } + + public static void placeCenteredBottom(WorldGenLevel world, + BlockPos pos, + StructureTemplate structure, + Rotation rotation, + Mirror mirror, + BoundingBox bounds, + RandomSource random) { + BlockPos offset = offsetPos(pos, structure, rotation, mirror); + StructurePlaceSettings placementData = new StructurePlaceSettings().setRotation(rotation) + .setMirror(mirror) + .setBoundingBox(bounds); + structure.placeInWorld(world, offset, offset, placementData, random, 4); + } + + private static BoundingBox makeBox(BlockPos pos) { + int sx = ((pos.getX() >> 4) << 4) - 16; + int sz = ((pos.getZ() >> 4) << 4) - 16; + int ex = sx + 47; + int ez = sz + 47; + return BoundingBox.fromCorners(new Vec3i(sx, 0, sz), new Vec3i(ex, 255, ez)); + } + + public static BoundingBox getStructureBounds(BlockPos pos, + StructureTemplate structure, + Rotation rotation, + Mirror mirror) { + Vec3i max = structure.getSize(); + Vec3 min = StructureTemplate.transform(Vec3.atCenterOf(structure.getSize()), mirror, rotation, BlockPos.ZERO); + max = max.offset(-min.x, -min.y, -min.z); + return BoundingBox.fromCorners(pos.offset(min.x, min.y, min.z), max.offset(pos)); + } + + public static BoundingBox intersectBoxes(BoundingBox box1, BoundingBox box2) { + int x1 = MHelper.max(box1.minX(), box2.minX()); + int y1 = MHelper.max(box1.minY(), box2.minY()); + int z1 = MHelper.max(box1.minZ(), box2.minZ()); + + int x2 = MHelper.min(box1.maxX(), box2.maxX()); + int y2 = MHelper.min(box1.maxY(), box2.maxY()); + int z2 = MHelper.min(box1.maxZ(), box2.maxZ()); + + return BoundingBox.fromCorners(new Vec3i(x1, y1, z1), new Vec3i(x2, y2, z2)); + } +} diff --git a/src/main/java/org/betterx/bclib/util/TranslationHelper.java b/src/main/java/org/betterx/bclib/util/TranslationHelper.java new file mode 100644 index 00000000..28339d19 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/TranslationHelper.java @@ -0,0 +1,124 @@ +package org.betterx.bclib.util; + +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceLocation; + +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Set; + +public class TranslationHelper { + /** + * Print English translation file lines. Translation is "auto-beautified" text (example "strange_thing" -> "Strange Thing"). + * + * @param modID {@link String} mod ID string. + */ + public static void printMissingEnNames(String modID) { + printMissingNames(modID, "en_us"); + } + + /** + * Prints translation file lines for specified language. + * + * @param modID {@link String} mod ID string; + * @param languageCode {@link String} language code (example "en_us", "ru_ru"). + */ + public static void printMissingNames(String modID, String languageCode) { + Set missingNames = Sets.newHashSet(); + + Gson gson = new Gson(); + InputStream inputStream = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/" + languageCode + ".json"); + 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)) { + String name = block.getName().getString(); + if (!translation.has(name)) { + missingNames.add(name); + } + } + }); + + Registry.ITEM.forEach(item -> { + if (Registry.ITEM.getKey(item).getNamespace().equals(modID)) { + String name = item.getDescription().getString(); + if (!translation.has(name)) { + missingNames.add(name); + } + } + }); + + BuiltinRegistries.BIOME.forEach(biome -> { + ResourceLocation id = BuiltinRegistries.BIOME.getKey(biome); + if (id.getNamespace().equals(modID)) { + String name = "biome." + modID + "." + id.getPath(); + if (!translation.has(name)) { + missingNames.add(name); + } + } + }); + + Registry.ENTITY_TYPE.forEach((entity) -> { + ResourceLocation id = Registry.ENTITY_TYPE.getKey(entity); + if (id.getNamespace().equals(modID)) { + String name = "entity." + modID + "." + id.getPath(); + if (!translation.has(name)) { + missingNames.add(name); + } + } + }); + + if (!missingNames.isEmpty()) { + + System.out.println("========================================"); + System.out.println(" MISSING NAMES LIST"); + + if (!missingNames.isEmpty()) { + if (languageCode.equals("en_us")) { + System.out.println("========================================"); + System.out.println(" AUTO ENGLISH BEAUTIFICATION"); + System.out.println("========================================"); + missingNames.stream().sorted().forEach(name -> { + System.out.println(" \"" + name + "\": \"" + fastTranslateEn(name) + "\","); + }); + } else { + System.out.println("========================================"); + System.out.println(" TEMPLATE: [" + languageCode + "]"); + System.out.println("========================================"); + missingNames.stream().sorted().forEach(name -> { + System.out.println(" \"" + name + "\": \"\","); + }); + } + } + + System.out.println("========================================"); + } + } + + /** + * Simple fast text beautification (example "strange_thing" -> "Strange Thing"). + * + * @param text {@link String} to process; + * @return {@link String} result. + */ + public static String fastTranslateEn(String text) { + String[] words = text.substring(text.lastIndexOf('.') + 1).split("_"); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < words.length; i++) { + String word = words[i]; + builder.append(Character.toUpperCase(word.charAt(0))); + builder.append(word, 1, word.length()); + if (i < words.length - 1) { + builder.append(' '); + } + } + return builder.toString(); + } +} diff --git a/src/main/java/ru/bclib/util/TriFunction.java b/src/main/java/org/betterx/bclib/util/TriFunction.java similarity index 52% rename from src/main/java/ru/bclib/util/TriFunction.java rename to src/main/java/org/betterx/bclib/util/TriFunction.java index d58760d3..6e568e13 100644 --- a/src/main/java/ru/bclib/util/TriFunction.java +++ b/src/main/java/org/betterx/bclib/util/TriFunction.java @@ -1,6 +1,6 @@ -package ru.bclib.util; +package org.betterx.bclib.util; @FunctionalInterface public interface TriFunction { - R apply(A a, B b, C c); + R apply(A a, B b, C c); } diff --git a/src/main/java/org/betterx/bclib/util/Triple.java b/src/main/java/org/betterx/bclib/util/Triple.java new file mode 100644 index 00000000..b47d5a81 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/Triple.java @@ -0,0 +1,31 @@ +package org.betterx.bclib.util; + +import java.util.Objects; + +public class Triple extends Pair { + public final C third; + + public Triple(A first, B second, C third) { + super(first, second); + this.third = third; + } + + @Override + public String toString() { + return "Triple{" + "first=" + first + ", second=" + second + ", third=" + third + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Triple)) return false; + if (!super.equals(o)) return false; + Triple triple = (Triple) o; + return Objects.equals(third, triple.third); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), third); + } +} diff --git a/src/main/java/org/betterx/bclib/util/WeighTree.java b/src/main/java/org/betterx/bclib/util/WeighTree.java new file mode 100644 index 00000000..f02655ff --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/WeighTree.java @@ -0,0 +1,91 @@ +package org.betterx.bclib.util; + +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import java.util.Locale; +import java.util.Random; + +public class WeighTree { + private final float maxWeight; + private final Node root; + + public WeighTree(WeightedList list) { + maxWeight = list.getMaxWeight(); + root = getNode(list); + } + + /** + * Get eandom value from tree. + * + * @param random - {@link Random}. + * @return {@link T} value. + */ + public T get(WorldgenRandom random) { + return root.get(random.nextFloat() * maxWeight); + } + + private Node getNode(WeightedList list) { + int size = list.size(); + if (size == 1) { + return new Leaf(list.get(0)); + } else if (size == 2) { + T first = list.get(0); + return new Branch(list.getWeight(0), new Leaf(first), new Leaf(list.get(1))); + } else { + int index = size >> 1; + float separator = list.getWeight(index); + Node a = getNode(list.subList(0, index + 1)); + Node b = getNode(list.subList(index, size)); + return new Branch(separator, a, b); + } + } + + private abstract class Node { + abstract T get(float value); + } + + private class Branch extends Node { + final float separator; + final Node min; + final Node max; + + public Branch(float separator, Node min, Node max) { + this.separator = separator; + this.min = min; + this.max = max; + } + + @Override + T get(float value) { + return value < separator ? min.get(value) : max.get(value); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "[%f, %s, %s]", separator, min.toString(), max.toString()); + } + } + + private class Leaf extends Node { + final T biome; + + Leaf(T value) { + this.biome = value; + } + + @Override + T get(float value) { + return biome; + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "[%s]", biome.toString()); + } + } + + @Override + public String toString() { + return root.toString(); + } +} diff --git a/src/main/java/org/betterx/bclib/util/WeightedList.java b/src/main/java/org/betterx/bclib/util/WeightedList.java new file mode 100644 index 00000000..70a90f4a --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/WeightedList.java @@ -0,0 +1,127 @@ +package org.betterx.bclib.util; + +import net.minecraft.util.RandomSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; + +public class WeightedList { + private final List weights = new ArrayList(); + private final List values = new ArrayList(); + private float maxWeight; + + /** + * Adds value with specified weight to the list + * + * @param value + * @param weight + */ + public void add(T value, float weight) { + maxWeight += weight; + weights.add(maxWeight); + values.add(value); + } + + /** + * Get random value. + * + * @param random - {@link Random}. + * @return {@link T} value. + */ + public T get(RandomSource random) { + if (maxWeight < 1) { + return null; + } + float weight = random.nextFloat() * maxWeight; + for (int i = 0; i < weights.size(); i++) { + if (weight <= weights.get(i)) { + return values.get(i); + } + } + return null; + } + + /** + * Get value by index. + * + * @param index - {@code int} index. + * @return {@link T} value. + */ + public T get(int index) { + return values.get(index); + } + + /** + * Get value weight. Weight is summed with all previous values weights. + * + * @param index - {@code int} index. + * @return {@code float} weight. + */ + public float getWeight(int index) { + return weights.get(index); + } + + /** + * Chech if the list is empty. + * + * @return {@code true} if list is empty and {@code false} if not. + */ + public boolean isEmpty() { + return maxWeight == 0; + } + + /** + * Get the list size. + * + * @return {@code int} list size. + */ + public int size() { + return values.size(); + } + + /** + * Makes a sublist of this list with same weights. Used only in {@link WeighTree} + * + * @param start - {@code int} start index (inclusive). + * @param end - {@code int} end index (exclusive). + * @return {@link WeightedList}. + */ + protected WeightedList subList(int start, int end) { + WeightedList list = new WeightedList(); + for (int i = start; i < end; i++) { + list.weights.add(weights.get(i)); + list.values.add(values.get(i)); + } + return list; + } + + /** + * Check if list contains certain value. + * + * @param value - {@link T} value. + * @return {@code true} if value is in list and {@code false} if not. + */ + public boolean contains(T value) { + return values.contains(value); + } + + /** + * Applies {@link Consumer} to all values in list. + * + * @param function - {@link Consumer}. + */ + public void forEach(Consumer function) { + values.forEach(function); + } + + /** + * Get the maximum weight of the tree. + * + * @return {@code float} maximum weight. + */ + public float getMaxWeight() { + return maxWeight; + } +} diff --git a/src/main/java/org/betterx/bclib/world/biomes/BCLBiome.java b/src/main/java/org/betterx/bclib/world/biomes/BCLBiome.java new file mode 100644 index 00000000..9c51feaf --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/biomes/BCLBiome.java @@ -0,0 +1,361 @@ +package org.betterx.bclib.world.biomes; + +import net.minecraft.core.Holder; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Climate; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.biomes.BCLBiomeBuilder; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.util.WeightedList; + +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.Consumer; +import org.jetbrains.annotations.Nullable; + +public class BCLBiome extends BCLBiomeSettings { + private final Set> structureTags = Sets.newHashSet(); + private final WeightedList subbiomes = new WeightedList<>(); + private final Map customData = Maps.newHashMap(); + private final ResourceLocation biomeID; + private final Biome biome; + + private final List parameterPoints = Lists.newArrayList(); + + private Consumer> surfaceInit; + private BCLBiome biomeParent; + + /** + * Create wrapper for existing biome using its {@link ResourceLocation} identifier. + * + * @param biomeKey {@link ResourceKey} for the {@link Biome}. + */ + public BCLBiome(ResourceKey biomeKey) { + this(biomeKey.location()); + } + + /** + * Create wrapper for existing biome using its {@link ResourceLocation} identifier. + * + * @param biomeID {@link ResourceLocation} biome ID. + */ + public BCLBiome(ResourceLocation biomeID) { + this(biomeID, BuiltinRegistries.BIOME.get(biomeID), null); + } + + /** + * Create wrapper for existing biome using biome instance from {@link BuiltinRegistries}. + * + * @param biome {@link Biome} to wrap. + */ + public BCLBiome(Biome biome) { + this(biome, null); + } + + /** + * Create wrapper for existing biome using biome instance from {@link BuiltinRegistries}. + * + * @param biome {@link Biome} to wrap. + * @param settings The Settings for this Biome or {@code null} if you want to apply default settings + */ + public BCLBiome(Biome biome, VanillaBiomeSettings settings) { + this(BiomeAPI.getBiomeID(biome), biome, settings); + } + + public BCLBiome(ResourceLocation biomeID, Biome biome) { + this(biomeID, biome, null); + } + + /** + * Create a new Biome + * + * @param biomeID {@link ResourceLocation} biome ID. + * @param biome {@link Biome} to wrap. + * @param defaults The Settings for this Biome or null if you want to apply the defaults + */ + public BCLBiome(ResourceLocation biomeID, Biome biome, BCLBiomeSettings defaults) { + this.subbiomes.add(this, 1.0F); + this.biomeID = biomeID; + this.biome = biome; + + if (defaults != null) { + defaults.applyWithDefaults(this); + } + } + + /** + * Get current biome edge. + * + * @return {@link BCLBiome} edge. + */ + @Nullable + public BCLBiome getEdge() { + return edge; + } + + /** + * Set biome edge for this biome instance. + * + * @param edge {@link BCLBiome} as the edge biome. + * @return same {@link BCLBiome}. + */ + BCLBiome setEdge(BCLBiome edge) { + this.edge = edge; + edge.biomeParent = this; + return this; + } + + /** + * Adds sub-biome into this biome instance. Biome chance will be interpreted as a sub-biome generation chance. + * Biome itself has chance 1.0 compared to all its sub-biomes. + * + * @param biome {@link Random} to be added. + * @return same {@link BCLBiome}. + */ + public BCLBiome addSubBiome(BCLBiome biome) { + biome.biomeParent = this; + subbiomes.add(biome, biome.getGenChance()); + return this; + } + + /** + * Checks if specified biome is a sub-biome of this one. + * + * @param biome {@link Random}. + * @return true if this instance contains specified biome as a sub-biome. + */ + public boolean containsSubBiome(BCLBiome biome) { + return subbiomes.contains(biome); + } + + /** + * Getter for a random sub-biome from all existing sub-biomes. Will return biome itself if there are no sub-biomes. + * + * @param random {@link Random}. + * @return {@link BCLBiome}. + */ + public BCLBiome getSubBiome(WorldgenRandom random) { + return subbiomes.get(random); + } + + public void forEachSubBiome(BiConsumer consumer) { + for (int i = 0; i < subbiomes.size(); i++) + consumer.accept(subbiomes.get(i), subbiomes.getWeight(i)); + } + + /** + * Getter for parent {@link BCLBiome} or null if there are no parent biome. + * + * @return {@link BCLBiome} or null. + */ + @Nullable + public BCLBiome getParentBiome() { + return this.biomeParent; + } + + /** + * Compares biome instances (directly) and their parents. Used in custom world generator. + * + * @param biome {@link BCLBiome} + * @return true if biome or its parent is same. + */ + public boolean isSame(BCLBiome biome) { + return biome == this || (biome.biomeParent != null && biome.biomeParent == this); + } + + /** + * Getter for biome identifier. + * + * @return {@link ResourceLocation} + */ + public ResourceLocation getID() { + return biomeID; + } + + + public Holder getBiomeHolder() { + return BuiltinRegistries.BIOME.getOrCreateHolder(BiomeAPI.getBiomeKey(biome)); + } + + /** + * Getter for biome from buil-in registry. For datapack biomes will be same as actual biome. + * + * @return {@link Biome}. + */ + public Biome getBiome() { + return biome; + } + +// /** +// * Recursively update biomes to correct world biome registry instances, for internal usage only. +// * @param biomeRegistry {@link Registry} for {@link Biome}. +// */ +// public void updateActualBiomes(Registry biomeRegistry) { +// subbiomes.forEach((sub) -> { +// if (sub != this) { +// sub.updateActualBiomes(biomeRegistry); +// } +// }); +// if (edge != null && edge != this) { +// edge.updateActualBiomes(biomeRegistry); +// } +// +// final ResourceKey key = biomeRegistry.getResourceKey(biomeRegistry.get(biomeID)).orElseThrow(); +// Holder aBiome = biomeRegistry.getOrCreateHolder(key); +// if (aBiome != actualBiome && actualBiome != null) { +// System.out.println("Changed actual Biome"); +// } +// this.actualBiome = aBiome; +// if (actualBiome == null) { +// BCLib.LOGGER.error("Unable to find actual Biome for " + biomeID); +// } +// } + + /** + * For internal use from BiomeAPI only + */ + public void afterRegistration() { + if (!this.structureTags.isEmpty()) { + structureTags.forEach(tagKey -> + TagAPI.addBiomeTag(tagKey, biome) + ); + } + + if (this.surfaceInit != null) { + surfaceInit.accept(getBiomeHolder()); + } + } + + + /** + * Getter for custom data. Will get custom data object or null if object doesn't exists. + * + * @param name {@link String} name of data object. + * @return object value or null. + */ + @Nullable + @SuppressWarnings("unchecked") + @Deprecated(forRemoval = true) + public T getCustomData(String name) { + return (T) customData.get(name); + } + + /** + * Getter for custom data. Will get custom data object or default value if object doesn't exists. + * + * @param name {@link String} name of data object. + * @param defaultValue object default value. + * @return object value or default value. + */ + @SuppressWarnings("unchecked") + @Deprecated(forRemoval = true) + public T getCustomData(String name, T defaultValue) { + return (T) customData.getOrDefault(name, defaultValue); + } + + /** + * Adds custom data object to this biome instance. + * + * @param name {@link String} name of data object. + * @param obj any data to add. + * @return same {@link BCLBiome}. + */ + public BCLBiome addCustomData(String name, Object obj) { + customData.put(name, obj); + return this; + } + + /** + * Adds custom data object to this biome instance. + * + * @param data a {@link Map} with custom data. + * @return same {@link BCLBiome}. + */ + public BCLBiome addCustomData(Map data) { + customData.putAll(data); + return this; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + BCLBiome biome = (BCLBiome) obj; + return biome != null && biomeID.equals(biome.biomeID); + } + + @Override + public int hashCode() { + return biomeID.hashCode(); + } + + @Override + public String toString() { + return biomeID.toString(); + } + + /** + * Adds structures to this biome. For internal use only. + * Used inside {@link BCLBiomeBuilder}. + */ + public void attachStructures(List> structures) { + this.structureTags.addAll(structures); + } + + /** + * Adds structures to this biome. For internal use only. + * Used inside {@link BCLBiomeBuilder}. + */ + public void addClimateParameters(List params) { + this.parameterPoints.addAll(params); + } + + public void forEachClimateParameter(Consumer consumer) { + this.parameterPoints.forEach(consumer); + } + + /** + * Sets biome surface rule. + * + * @param surface {@link SurfaceRules.RuleSource} rule. + */ + public void setSurface(RuleSource surface) { + this.surfaceInit = (b) -> { + final ResourceKey key = BiomeAPI.getBiomeKey(b); + if (key == null) { + BCLib.LOGGER.warning("BCL Biome " + biomeID + " does not have registry key!"); + } else { + BiomeAPI.addSurfaceRule(biomeID, SurfaceRules.ifTrue(SurfaceRules.isBiome(key), surface)); + } + }; + } + + /** + * Returns the group used in the config Files for this biome + *

+ * Example: {@code Configs.BIOMES_CONFIG.getFloat(configGroup(), "generation_chance", 1.0);} + * + * @return The group name + */ + public String configGroup() { + return biomeID.getNamespace() + "." + biomeID.getPath(); + } + + private final boolean didLoadConfig = false; +} diff --git a/src/main/java/org/betterx/bclib/world/biomes/BCLBiomeSettings.java b/src/main/java/org/betterx/bclib/world/biomes/BCLBiomeSettings.java new file mode 100644 index 00000000..d7d7cd03 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/biomes/BCLBiomeSettings.java @@ -0,0 +1,202 @@ +package org.betterx.bclib.world.biomes; + +import net.minecraft.world.level.biome.Biome; + +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.world.generator.BiomePicker; + +public class BCLBiomeSettings { + public static Builder createBCL() { + return new Builder(); + } + + public static class Builder extends CommonBuilder { + public Builder() { + super(new BCLBiomeSettings()); + } + } + + public static class CommonBuilder { + private final T storage; + + CommonBuilder(T storage) { + this.storage = storage; + } + + public T build() { + return storage; + } + + /** + * Set gen chance for this biome, default value is 1.0. + * + * @param genChance chance of this biome to be generated. + * @return same {@link BCLBiomeSettings}. + */ + public R setGenChance(float genChance) { + storage.genChance = genChance; + return (R) this; + } + + /** + * Setter for terrain height, can be used in custom terrain generator. + * + * @param terrainHeight a relative float terrain height value. + * @return same {@link Builder}. + */ + public R setTerrainHeight(float terrainHeight) { + storage.terrainHeight = terrainHeight; + return (R) this; + } + + /** + * Set biome vertical distribution (for tall Nether only). + * + * @return same {@link Builder}. + */ + public R setVertical() { + return setVertical(true); + } + + /** + * Set biome vertical distribution (for tall Nether only). + * + * @param vertical {@code boolean} value. + * @return same {@link Builder}. + */ + public R setVertical(boolean vertical) { + storage.vertical = vertical; + return (R) this; + } + + /** + * Set edges size for this biome. Size is in blocks. + * + * @param size as a float value. + * @return same {@link Builder}. + */ + public R setEdgeSize(int size) { + storage.edgeSize = size; + return (R) this; + } + + /** + * Set edges:biome for this biome. + * + * @param edge The {@link Biome}. + * @return same {@link Builder}. + */ + public R setEdge(BCLBiome edge) { + storage.edge = edge; + return (R) this; + } + + /** + * Sets fog density for this biome. + * + * @param fogDensity + * @return same {@link Builder}. + */ + public R setFogDensity(float fogDensity) { + storage.fogDensity = fogDensity; + return (R) this; + } + } + + protected BCLBiomeSettings() { + this.terrainHeight = 0.1F; + this.fogDensity = 1.0F; + this.genChance = 1.0F; + this.edgeSize = 0; + this.vertical = false; + this.edge = null; + } + + float terrainHeight; + float fogDensity; + float genChance; + int edgeSize; + boolean vertical; + BCLBiome edge; + + + /** + * Getter for biome generation chance, used in {@link BiomePicker} and in custom generators. + * + * @return biome generation chance as float. + */ + public float getGenChance() { + return this.genChance; + } + + /** + * Checks if biome is vertical, for tall Nether only (or for custom generators). + * + * @return is biome vertical or not. + */ + public boolean isVertical() { + return vertical; + } + + /** + * Getter for terrain height, can be used in custom terrain generator. + * + * @return terrain height. + */ + public float getTerrainHeight() { + return terrainHeight; + } + + /** + * Getter for fog density, used in custom for renderer. + * + * @return fog density as a float. + */ + public float getFogDensity() { + return fogDensity; + } + + /** + * Getter for biome edge size. + * + * @return edge size in blocks. + */ + public int getEdgeSize() { + return edgeSize; + } + + /** + * Getter for edge-biome. + * + * @return The assigned edge biome. + */ + public BCLBiome getEdge() { + return edge; + } + + /** + * Load values from Config and apply to the passed Biome. The Default values for the loaded settings + * are derifed from the values store in this object + * + * @param biome {@link BCLBiome} to assign values to + */ + public void applyWithDefaults(BCLBiome biome) { + final String group = biome.configGroup(); + biome.genChance = Configs.BIOMES_CONFIG.getFloat(group, "generation_chance", this.genChance); + + if (edge != null) { + biome.edgeSize = Configs.BIOMES_CONFIG.getInt(group, "edge_size", this.edgeSize); + if (edgeSize > 0) { + biome.setEdge(edge); + } + } + + if (!(this instanceof VanillaBiomeSettings)) { + biome.fogDensity = Configs.BIOMES_CONFIG.getFloat(group, "fog_density", this.fogDensity); + biome.vertical = Configs.BIOMES_CONFIG.getBoolean(group, "vertical", this.vertical); + biome.terrainHeight = Configs.BIOMES_CONFIG.getFloat(group, "terrain_height", this.terrainHeight); + } + + Configs.BIOMES_CONFIG.saveChanges(); + } +} diff --git a/src/main/java/org/betterx/bclib/world/biomes/FabricBiomesData.java b/src/main/java/org/betterx/bclib/world/biomes/FabricBiomesData.java new file mode 100644 index 00000000..ab2ce718 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/biomes/FabricBiomesData.java @@ -0,0 +1,16 @@ +package org.betterx.bclib.world.biomes; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import java.util.Map; +import java.util.Set; + +public class FabricBiomesData { + public static final Map, Float> END_LAND_BIOMES = Maps.newHashMap(); + public static final Map, Float> END_VOID_BIOMES = Maps.newHashMap(); + public static final Set> NETHER_BIOMES = Sets.newHashSet(); +} diff --git a/src/main/java/org/betterx/bclib/world/biomes/VanillaBiomeSettings.java b/src/main/java/org/betterx/bclib/world/biomes/VanillaBiomeSettings.java new file mode 100644 index 00000000..0fa9c259 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/biomes/VanillaBiomeSettings.java @@ -0,0 +1,14 @@ +package org.betterx.bclib.world.biomes; + + +public class VanillaBiomeSettings extends BCLBiomeSettings { + public static class Builder extends BCLBiomeSettings.CommonBuilder { + public Builder() { + super(new VanillaBiomeSettings()); + } + } + + public static Builder createVanilla() { + return new Builder(); + } +} diff --git a/src/main/java/ru/bclib/world/features/BCLDecorators.java b/src/main/java/org/betterx/bclib/world/features/BCLDecorators.java similarity index 93% rename from src/main/java/ru/bclib/world/features/BCLDecorators.java rename to src/main/java/org/betterx/bclib/world/features/BCLDecorators.java index ca8f377d..8fbf6878 100644 --- a/src/main/java/ru/bclib/world/features/BCLDecorators.java +++ b/src/main/java/org/betterx/bclib/world/features/BCLDecorators.java @@ -1,4 +1,4 @@ -package ru.bclib.world.features; +package org.betterx.bclib.world.features; public class BCLDecorators { /*public static final ConfiguredDecorator HEIGHTMAP_SQUARE; diff --git a/src/main/java/org/betterx/bclib/world/features/BCLFeature.java b/src/main/java/org/betterx/bclib/world/features/BCLFeature.java new file mode 100644 index 00000000..3f9679bd --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/features/BCLFeature.java @@ -0,0 +1,108 @@ +package org.betterx.bclib.world.features; + +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.data.worldgen.features.FeatureUtils; +import net.minecraft.data.worldgen.placement.PlacementUtils; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraft.world.level.levelgen.placement.PlacementModifier; + +import java.util.Map.Entry; +import java.util.Optional; + +public class BCLFeature { + private final Holder placedFeature; + private final Decoration featureStep; + private final Feature feature; + + + public > BCLFeature(ResourceLocation id, + F feature, + Decoration featureStep, + FC configuration, + PlacementModifier[] modifiers) { + this(id, feature, featureStep, buildPlacedFeature(id, feature, configuration, modifiers)); + } + + public BCLFeature(ResourceLocation id, + Feature feature, + Decoration featureStep, + Holder placedFeature) { + this.placedFeature = placedFeature; + this.featureStep = featureStep; + this.feature = feature; + + if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) { + Registry.register(BuiltinRegistries.PLACED_FEATURE, id, placedFeature.value()); + } + if (!Registry.FEATURE.containsKey(id) && !containsObj(Registry.FEATURE, feature)) { + Registry.register(Registry.FEATURE, id, feature); + } + } + + private static > Holder buildPlacedFeature( + ResourceLocation id, + F feature, + FC configuration, + PlacementModifier[] modifiers) { + Holder> configuredFeature; + if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) { + configuredFeature = (Holder>) (Object) FeatureUtils.register(id.toString(), + feature, + configuration); + } else { + configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(), + id)).orElseThrow(); + } + + if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) { + return PlacementUtils.register(id.toString(), configuredFeature, modifiers); + } else { + return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(), + id)).orElseThrow(); + } + } + + private static boolean containsObj(Registry registry, E obj) { + Optional, E>> optional = registry + .entrySet() + .stream() + .filter(entry -> entry.getValue() == obj) + .findAny(); + return optional.isPresent(); + } + + /** + * Get raw feature. + * + * @return {@link Feature}. + */ + public Feature getFeature() { + return feature; + } + + /** + * Get configured feature. + * + * @return {@link PlacedFeature}. + */ + public Holder getPlacedFeature() { + return placedFeature; + } + + /** + * Get feature decoration step. + * + * @return {@link Decoration}. + */ + public Decoration getDecoration() { + return featureStep; + } +} diff --git a/src/main/java/org/betterx/bclib/world/features/DefaultFeature.java b/src/main/java/org/betterx/bclib/world/features/DefaultFeature.java new file mode 100644 index 00000000..380b14af --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/features/DefaultFeature.java @@ -0,0 +1,45 @@ +package org.betterx.bclib.world.features; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; + +import org.betterx.bclib.util.BlocksHelper; + +public abstract class DefaultFeature extends Feature { + protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); + protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); + + public DefaultFeature() { + super(NoneFeatureConfiguration.CODEC); + } + + public static int getYOnSurface(WorldGenLevel world, int x, int z) { + return world.getHeight(Types.WORLD_SURFACE, x, z); + } + + public static int getYOnSurfaceWG(WorldGenLevel world, int x, int z) { + return world.getHeight(Types.WORLD_SURFACE_WG, x, z); + } + + public static BlockPos getPosOnSurface(WorldGenLevel world, BlockPos pos) { + return world.getHeightmapPos(Types.WORLD_SURFACE, pos); + } + + public static BlockPos getPosOnSurfaceWG(WorldGenLevel world, BlockPos pos) { + return world.getHeightmapPos(Types.WORLD_SURFACE_WG, pos); + } + + public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos) { + return getPosOnSurfaceRaycast(world, pos, 256); + } + + public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos, int dist) { + int h = BlocksHelper.downRay(world, pos, dist); + return pos.below(h); + } +} diff --git a/src/main/java/org/betterx/bclib/world/features/ListFeature.java b/src/main/java/org/betterx/bclib/world/features/ListFeature.java new file mode 100644 index 00000000..0ca3fee8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/features/ListFeature.java @@ -0,0 +1,82 @@ +package org.betterx.bclib.world.features; + +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; + +import org.betterx.bclib.util.StructureHelper; + +import java.util.List; + +public class ListFeature extends NBTFeature { + private final List list; + private StructureInfo selected; + + public ListFeature(List list, BlockState defaultBlock) { + super(defaultBlock); + this.list = list; + } + + @Override + protected StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, RandomSource random) { + selected = list.get(random.nextInt(list.size())); + return selected.getStructure(); + } + + @Override + protected boolean canSpawn(WorldGenLevel world, BlockPos pos, RandomSource random) { + int cx = pos.getX() >> 4; + int cz = pos.getZ() >> 4; + return ((cx + cz) & 1) == 0 && pos.getY() > 58;// && world.getBlockState(pos.below()).is(EndTags.GEN_TERRAIN); + } + + @Override + protected Rotation getRotation(WorldGenLevel world, BlockPos pos, RandomSource random) { + return Rotation.getRandom(random); + } + + @Override + protected Mirror getMirror(WorldGenLevel world, BlockPos pos, RandomSource random) { + return Mirror.values()[random.nextInt(3)]; + } + + @Override + protected int getYOffset(StructureTemplate structure, WorldGenLevel world, BlockPos pos, RandomSource random) { + return selected.offsetY; + } + + @Override + protected TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, RandomSource random) { + return selected.terrainMerge; + } + + @Override + protected void addStructureData(StructurePlaceSettings data) { + } + + public static final class StructureInfo { + public final TerrainMerge terrainMerge; + public final String structurePath; + public final int offsetY; + + private StructureTemplate structure; + + public StructureInfo(String structurePath, int offsetY, TerrainMerge terrainMerge) { + this.terrainMerge = terrainMerge; + this.structurePath = structurePath; + this.offsetY = offsetY; + } + + public StructureTemplate getStructure() { + if (structure == null) { + structure = StructureHelper.readStructure(structurePath); + } + return structure; + } + } +} diff --git a/src/main/java/org/betterx/bclib/world/features/NBTFeature.java b/src/main/java/org/betterx/bclib/world/features/NBTFeature.java new file mode 100644 index 00000000..751b5219 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/features/NBTFeature.java @@ -0,0 +1,234 @@ +package org.betterx.bclib.world.features; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; + +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.api.tag.CommonBlockTags; +import org.betterx.bclib.util.BlocksHelper; +import org.betterx.bclib.world.processors.DestructionStructureProcessor; + +import java.io.IOException; +import java.io.InputStream; + +public abstract class NBTFeature extends DefaultFeature { + private final BlockState defaultBlock; + + public NBTFeature(BlockState defaultBlock) { + this.defaultBlock = defaultBlock; + } + + protected static final DestructionStructureProcessor DESTRUCTION = new DestructionStructureProcessor(); + + protected abstract StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, RandomSource random); + + protected abstract boolean canSpawn(WorldGenLevel world, BlockPos pos, RandomSource random); + + protected abstract Rotation getRotation(WorldGenLevel world, BlockPos pos, RandomSource random); + + protected abstract Mirror getMirror(WorldGenLevel world, BlockPos pos, RandomSource random); + + protected abstract int getYOffset(StructureTemplate structure, + WorldGenLevel world, + BlockPos pos, + RandomSource random); + + protected abstract TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, RandomSource random); + + protected abstract void addStructureData(StructurePlaceSettings data); + + protected BlockPos getGround(WorldGenLevel world, BlockPos center) { + Holder biome = world.getBiome(center); + ResourceLocation id = BiomeAPI.getBiomeID(biome); + if (id.getNamespace().contains("moutain") || id.getNamespace().contains("lake")) { + int y = getAverageY(world, center); + return new BlockPos(center.getX(), y, center.getZ()); + } else { + int y = getAverageYWG(world, center); + return new BlockPos(center.getX(), y, center.getZ()); + } + } + + protected int getAverageY(WorldGenLevel world, BlockPos center) { + int y = getYOnSurface(world, center.getX(), center.getZ()); + y += getYOnSurface(world, center.getX() - 2, center.getZ() - 2); + y += getYOnSurface(world, center.getX() + 2, center.getZ() - 2); + y += getYOnSurface(world, center.getX() - 2, center.getZ() + 2); + y += getYOnSurface(world, center.getX() + 2, center.getZ() + 2); + return y / 5; + } + + protected int getAverageYWG(WorldGenLevel world, BlockPos center) { + int y = getYOnSurfaceWG(world, center.getX(), center.getZ()); + y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() - 2); + y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() - 2); + y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() + 2); + y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() + 2); + return y / 5; + } + + @Override + public boolean place(FeaturePlaceContext context) { + WorldGenLevel world = context.level(); + RandomSource random = context.random(); + BlockPos center = context.origin(); + + center = new BlockPos(((center.getX() >> 4) << 4) | 8, 128, ((center.getZ() >> 4) << 4) | 8); + center = getGround(world, center); + + if (!canSpawn(world, center, random)) { + return false; + } + + int posY = center.getY() + 1; + StructureTemplate structure = getStructure(world, center, random); + Rotation rotation = getRotation(world, center, random); + Mirror mirror = getMirror(world, center, random); + BlockPos offset = StructureTemplate.transform( + new BlockPos(structure.getSize()), + mirror, + rotation, + BlockPos.ZERO + ); + center = center.offset(0, getYOffset(structure, world, center, random) + 0.5, 0); + + BoundingBox bounds = makeBox(center); + StructurePlaceSettings placementData = new StructurePlaceSettings() + .setRotation(rotation) + .setMirror(mirror) + .setBoundingBox(bounds); + addStructureData(placementData); + center = center.offset(-offset.getX() * 0.5, 0, -offset.getZ() * 0.5); + structure.placeInWorld(world, center, center, placementData, random, 4); + + TerrainMerge merge = getTerrainMerge(world, center, random); + int x1 = center.getX(); + int z1 = center.getZ(); + int x2 = x1 + offset.getX(); + int z2 = z1 + offset.getZ(); + if (merge != TerrainMerge.NONE) { + MutableBlockPos mut = new MutableBlockPos(); + + if (x2 < x1) { + int a = x1; + x1 = x2; + x2 = a; + } + + if (z2 < z1) { + int a = z1; + z1 = z2; + z2 = a; + } + + int surfMax = posY - 1; + for (int x = x1; x <= x2; x++) { + mut.setX(x); + for (int z = z1; z <= z2; z++) { + mut.setZ(z); + mut.setY(surfMax); + BlockState state = world.getBlockState(mut); + if (!isTerrain(state) && state.isFaceSturdy(world, mut, Direction.DOWN)) { + for (int i = 0; i < 10; i++) { + mut.setY(mut.getY() - 1); + BlockState stateSt = world.getBlockState(mut); + if (!isTerrain(stateSt)) { + if (merge == TerrainMerge.SURFACE) { + boolean isTop = mut.getY() == surfMax && state.getMaterial().isSolidBlocking(); + Holder b = world.getBiome(mut); + BlockState top = (isTop + ? BiomeAPI.findTopMaterial(b) + : BiomeAPI.findUnderMaterial(b)).orElse(defaultBlock); + BlocksHelper.setWithoutUpdate(world, mut, top); + } else { + BlocksHelper.setWithoutUpdate(world, mut, state); + } + } else { + if (isTerrain(state) && state.getMaterial().isSolidBlocking()) { + if (merge == TerrainMerge.SURFACE) { + Holder b = world.getBiome(mut); + BlockState bottom = BiomeAPI.findUnderMaterial(b).orElse(defaultBlock); + BlocksHelper.setWithoutUpdate(world, mut, bottom); + } else { + BlocksHelper.setWithoutUpdate(world, mut, state); + } + } + break; + } + } + } + } + } + } + //BlocksHelper.fixBlocks(world, new BlockPos(x1, center.getY(), z1), new BlockPos(x2, center.getY() + offset.getY(), z2)); + + return true; + } + + private boolean isTerrain(BlockState state) { + return state.is(CommonBlockTags.END_STONES) || state.is(CommonBlockTags.NETHER_STONES); + } + + protected BoundingBox makeBox(BlockPos pos) { + int sx = ((pos.getX() >> 4) << 4) - 16; + int sz = ((pos.getZ() >> 4) << 4) - 16; + int ex = sx + 47; + int ez = sz + 47; + return BoundingBox.fromCorners(new Vec3i(sx, 0, sz), new Vec3i(ex, 255, ez)); + } + + protected static StructureTemplate readStructure(ResourceLocation resource) { + String ns = resource.getNamespace(); + String nm = resource.getPath(); + + try { + InputStream inputstream = MinecraftServer.class.getResourceAsStream("/data/" + ns + "/structures/" + nm + ".nbt"); + return readStructureFromStream(inputstream); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException { + CompoundTag nbttagcompound = NbtIo.readCompressed(stream); + + StructureTemplate template = new StructureTemplate(); + template.load(nbttagcompound); + + return template; + } + + public enum TerrainMerge { + NONE, SURFACE, OBJECT; + + public static TerrainMerge getFromString(String type) { + if (type.equals("surface")) { + return SURFACE; + } else if (type.equals("object")) { + return OBJECT; + } else { + return NONE; + } + } + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/BCLBiomeSource.java b/src/main/java/org/betterx/bclib/world/generator/BCLBiomeSource.java new file mode 100644 index 00000000..c27f8792 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/BCLBiomeSource.java @@ -0,0 +1,34 @@ +package org.betterx.bclib.world.generator; + +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; + +import org.betterx.bclib.api.biomes.BiomeAPI; + +import java.util.List; + +public abstract class BCLBiomeSource extends BiomeSource { + protected final Registry biomeRegistry; + protected long currentSeed; + + private static List> preInit(Registry biomeRegistry, List> biomes) { + biomes.forEach(biome -> BiomeAPI.sortBiomeFeatures(biome)); + return biomes; + } + + protected BCLBiomeSource(Registry biomeRegistry, List> list) { + super(preInit(biomeRegistry, list)); + System.out.println(this + " with Registry: " + biomeRegistry.getClass().getName() + "@" + Integer.toHexString( + biomeRegistry.hashCode())); + this.biomeRegistry = biomeRegistry; + + BiomeAPI.initRegistry(biomeRegistry); + } + + public void setSeed(long seed) { + System.out.println(this + " set Seed: " + seed); + this.currentSeed = seed; + } +} diff --git a/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java b/src/main/java/org/betterx/bclib/world/generator/BCLibEndBiomeSource.java similarity index 94% rename from src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java rename to src/main/java/org/betterx/bclib/world/generator/BCLibEndBiomeSource.java index ea45802b..8139c975 100644 --- a/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java +++ b/src/main/java/org/betterx/bclib/world/generator/BCLibEndBiomeSource.java @@ -1,4 +1,4 @@ -package ru.bclib.world.generator; +package org.betterx.bclib.world.generator; import net.minecraft.core.Holder; import net.minecraft.core.Registry; @@ -16,15 +16,15 @@ import net.minecraft.world.level.levelgen.synth.SimplexNoise; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import ru.bclib.BCLib; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.config.ConfigKeeper.StringArrayEntry; -import ru.bclib.config.Configs; -import ru.bclib.interfaces.BiomeMap; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.world.biomes.BCLBiome; -import ru.bclib.world.generator.map.hex.HexBiomeMap; -import ru.bclib.world.generator.map.square.SquareBiomeMap; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.config.ConfigKeeper.StringArrayEntry; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.interfaces.BiomeMap; +import org.betterx.bclib.noise.OpenSimplexNoise; +import org.betterx.bclib.world.biomes.BCLBiome; +import org.betterx.bclib.world.generator.map.hex.HexBiomeMap; +import org.betterx.bclib.world.generator.map.square.SquareBiomeMap; import java.awt.*; import java.util.List; @@ -49,7 +49,7 @@ public class BCLibEndBiomeSource extends BCLBiomeSource { private final Holder centerBiome; private final Holder barrens; private final Point pos; - private Function endLandFunction; + private final Function endLandFunction; private SimplexNoise noise; private BiomeMap mapLand; private BiomeMap mapVoid; @@ -128,7 +128,7 @@ public class BCLibEndBiomeSource extends BCLBiomeSource { return true; } - final boolean isEndBiome = biome.is(BiomeTags.IS_END) || + final boolean isEndBiome = biome.is(BiomeTags.IS_END) || BiomeAPI.wasRegisteredAsEndBiome(key); @@ -194,7 +194,7 @@ public class BCLibEndBiomeSource extends BCLBiomeSource { @Override public void setSeed(long seed) { - if (seed==currentSeed) return; + if (seed == currentSeed) return; super.setSeed(seed); initMap(seed); diff --git a/src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java b/src/main/java/org/betterx/bclib/world/generator/BCLibNetherBiomeSource.java similarity index 81% rename from src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java rename to src/main/java/org/betterx/bclib/world/generator/BCLibNetherBiomeSource.java index 0af0e2e4..c5c79cc8 100644 --- a/src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java +++ b/src/main/java/org/betterx/bclib/world/generator/BCLibNetherBiomeSource.java @@ -1,26 +1,28 @@ -package ru.bclib.world.generator; +package org.betterx.bclib.world.generator; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BiomeTags; -import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; import net.fabricmc.fabric.impl.biome.NetherBiomeData; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import org.apache.commons.lang3.function.TriFunction; -import ru.bclib.BCLib; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.config.ConfigKeeper.StringArrayEntry; -import ru.bclib.config.Configs; -import ru.bclib.interfaces.BiomeMap; -import ru.bclib.world.biomes.BCLBiome; -import ru.bclib.world.generator.map.MapStack; -import ru.bclib.world.generator.map.hex.HexBiomeMap; -import ru.bclib.world.generator.map.square.SquareBiomeMap; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.biomes.BiomeAPI; +import org.betterx.bclib.config.ConfigKeeper.StringArrayEntry; +import org.betterx.bclib.config.Configs; +import org.betterx.bclib.interfaces.BiomeMap; +import org.betterx.bclib.world.biomes.BCLBiome; +import org.betterx.bclib.world.generator.map.MapStack; +import org.betterx.bclib.world.generator.map.hex.HexBiomeMap; +import org.betterx.bclib.world.generator.map.square.SquareBiomeMap; import java.util.List; @@ -33,19 +35,20 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource { .group(RegistryOps .retrieveRegistry(Registry.BIOME_REGISTRY) .forGetter(source -> source.biomeRegistry), - Codec - .LONG - .fieldOf("seed") - .stable() - .forGetter(source -> { - return source.currentSeed; - }) - ) + Codec + .LONG + .fieldOf("seed") + .stable() + .forGetter(source -> { + return source.currentSeed; + }) + ) .apply(instance, instance.stable(BCLibNetherBiomeSource::new)) ); private BiomeMap biomeMap; private final BiomePicker biomePicker; - public BCLibNetherBiomeSource(Registry biomeRegistry) { + + public BCLibNetherBiomeSource(Registry biomeRegistry) { super(biomeRegistry, getBiomes(biomeRegistry)); biomePicker = new BiomePicker(biomeRegistry); @@ -66,7 +69,7 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource { }); biomePicker.rebuild(); - } + } public BCLibNetherBiomeSource(Registry biomeRegistry, long seed) { this(biomeRegistry); @@ -111,8 +114,8 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource { return NetherBiomeData.canGenerateInNether(biome.unwrapKey().get()) || - biome.is(BiomeTags.IS_NETHER) || - BiomeAPI.wasRegisteredAsNetherBiome(location); + biome.is(BiomeTags.IS_NETHER) || + BiomeAPI.wasRegisteredAsNetherBiome(location); }).toList(); } @@ -126,7 +129,7 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource { @Override public void setSeed(long seed) { - if (seed==currentSeed) return; + if (seed == currentSeed) return; super.setSeed(seed); initMap(seed); } @@ -175,6 +178,6 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource { @Override public String toString() { - return "BCLib - Nether BiomeSource ("+Integer.toHexString(hashCode())+")"; + return "BCLib - Nether BiomeSource (" + Integer.toHexString(hashCode()) + ")"; } } diff --git a/src/main/java/org/betterx/bclib/world/generator/BiomePicker.java b/src/main/java/org/betterx/bclib/world/generator/BiomePicker.java new file mode 100644 index 00000000..1f83684b --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/BiomePicker.java @@ -0,0 +1,108 @@ +package org.betterx.bclib.world.generator; + +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import com.google.common.collect.Lists; +import org.betterx.bclib.util.WeighTree; +import org.betterx.bclib.util.WeightedList; +import org.betterx.bclib.world.biomes.BCLBiome; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class BiomePicker { + public final Map all = new HashMap<>(); + + public class ActualBiome { + public final BCLBiome bclBiome; + public final Holder biome; + public final ResourceKey key; + + private final WeightedList subbiomes = new WeightedList<>(); + private final ActualBiome edge; + private final ActualBiome parent; + + private ActualBiome(BCLBiome bclBiome) { + all.put(bclBiome, this); + this.bclBiome = bclBiome; + + this.key = biomeRegistry.getResourceKey(biomeRegistry.get(bclBiome.getID())).orElseThrow(); + this.biome = biomeRegistry.getOrCreateHolder(key); + + bclBiome.forEachSubBiome((b, w) -> { + subbiomes.add(create(b), w); + }); + + edge = bclBiome.getEdge() != null ? create(bclBiome.getEdge()) : null; + parent = bclBiome.getParentBiome() != null ? create(bclBiome.getParentBiome()) : null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ActualBiome entry = (ActualBiome) o; + return bclBiome.equals(entry.bclBiome); + } + + @Override + public int hashCode() { + return Objects.hash(bclBiome); + } + + public ActualBiome getSubBiome(WorldgenRandom random) { + return subbiomes.get(random); + } + + public ActualBiome getEdge() { + return edge; + } + + public ActualBiome getParentBiome() { + return parent; + } + + public boolean isSame(ActualBiome e) { + return bclBiome.isSame(e.bclBiome); + } + } + + private ActualBiome create(BCLBiome bclBiome) { + ActualBiome e = all.get(bclBiome); + if (e != null) return e; + return new ActualBiome(bclBiome); + } + + private final List biomes = Lists.newArrayList(); + public final Registry biomeRegistry; + private WeighTree tree; + + public BiomePicker(Registry biomeRegistry) { + this.biomeRegistry = biomeRegistry; + } + + public void addBiome(BCLBiome biome) { + biomes.add(create(biome)); + } + + public ActualBiome getBiome(WorldgenRandom random) { + return biomes.isEmpty() ? null : tree.get(random); + } + + public void rebuild() { + if (biomes.isEmpty()) { + return; + } + WeightedList list = new WeightedList<>(); + biomes.forEach(biome -> { + list.add(biome, biome.bclBiome.getGenChance()); + }); + tree = new WeighTree<>(list); + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/BiomeType.java b/src/main/java/org/betterx/bclib/world/generator/BiomeType.java new file mode 100644 index 00000000..dd5bb0e0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/BiomeType.java @@ -0,0 +1,5 @@ +package org.betterx.bclib.world.generator; + +public enum BiomeType { + LAND, VOID +} diff --git a/src/main/java/org/betterx/bclib/world/generator/GeneratorOptions.java b/src/main/java/org/betterx/bclib/world/generator/GeneratorOptions.java new file mode 100644 index 00000000..61456b12 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/GeneratorOptions.java @@ -0,0 +1,108 @@ +package org.betterx.bclib.world.generator; + +import net.minecraft.util.Mth; + +import org.betterx.bclib.config.Configs; + +import java.awt.*; +import java.util.function.Function; + +public class GeneratorOptions { + private static int biomeSizeNether; + private static int biomeVSizeNether; + private static int biomeSizeEndLand; + private static int biomeSizeEndVoid; + private static Function endLandFunction; + private static boolean customNetherBiomeSource = true; + private static boolean customEndBiomeSource = true; + private static boolean useOldBiomeGenerator = false; + private static boolean verticalBiomes = true; + private static long farEndBiomesSqr = 1000000; + private static boolean fixEndBiomeSource = true; + private static boolean fixNetherBiomeSource = true; + + public static void init() { + biomeSizeNether = Configs.GENERATOR_CONFIG.getInt("nether.biomeMap", "biomeSize", 256); + biomeVSizeNether = Configs.GENERATOR_CONFIG.getInt("nether.biomeMap", + "biomeVerticalSize(onlyInTallNether)", + 86); + 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); + useOldBiomeGenerator = Configs.GENERATOR_CONFIG.useOldGenerator(); + verticalBiomes = Configs.GENERATOR_CONFIG.getBoolean("options", "verticalBiomesInTallNether", true); + fixEndBiomeSource = Configs.GENERATOR_CONFIG.getBoolean("options.biomeSource", "fixEndBiomeSource", true); + fixNetherBiomeSource = Configs.GENERATOR_CONFIG.getBoolean("options.biomeSource", "fixNetherBiomeSource", true); + } + + public static int getBiomeSizeNether() { + return Mth.clamp(biomeSizeNether, 1, 8192); + } + + public static int getVerticalBiomeSizeNether() { + return Mth.clamp(biomeVSizeNether, 1, 8192); + } + + public static int getBiomeSizeEndLand() { + return Mth.clamp(biomeSizeEndLand, 1, 8192); + } + + public static int getBiomeSizeEndVoid() { + return Mth.clamp(biomeSizeEndVoid, 1, 8192); + } + + public static void setEndLandFunction(Function endLandFunction) { + GeneratorOptions.endLandFunction = endLandFunction; + } + + public static Function getEndLandFunction() { + return endLandFunction; + } + + public static long getFarEndBiomes() { + return farEndBiomesSqr; + } + + /** + * Set distance of far End biomes generation, in blocks + * + * @param distance + */ + public static void setFarEndBiomes(int distance) { + GeneratorOptions.farEndBiomesSqr = (long) distance * (long) distance; + } + + /** + * Set distance of far End biomes generation, in blocks^2 + * + * @param distanceSqr the distance squared + */ + public static void setFarEndBiomesSqr(long distanceSqr) { + GeneratorOptions.farEndBiomesSqr = distanceSqr; + } + + public static boolean customNetherBiomeSource() { + return customNetherBiomeSource; + } + + public static boolean customEndBiomeSource() { + return customEndBiomeSource; + } + + public static boolean useOldBiomeGenerator() { + return useOldBiomeGenerator; + } + + public static boolean useVerticalBiomes() { + return verticalBiomes; + } + + public static boolean fixEndBiomeSource() { + return fixEndBiomeSource; + } + + public static boolean fixNetherBiomeSource() { + return fixNetherBiomeSource; + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/map/MapStack.java b/src/main/java/org/betterx/bclib/world/generator/map/MapStack.java new file mode 100644 index 00000000..1c924fb7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/map/MapStack.java @@ -0,0 +1,109 @@ +package org.betterx.bclib.world.generator.map; + +import net.minecraft.util.Mth; + +import org.apache.commons.lang3.function.TriFunction; +import org.betterx.bclib.interfaces.BiomeChunk; +import org.betterx.bclib.interfaces.BiomeMap; +import org.betterx.bclib.interfaces.TriConsumer; +import org.betterx.bclib.noise.OpenSimplexNoise; +import org.betterx.bclib.world.generator.BiomePicker; + +import java.util.Random; + +public class MapStack implements BiomeMap { + private final OpenSimplexNoise noise; + private final BiomeMap[] maps; + private final double layerDistortion; + private final int worldHeight; + private final int minValue; + private final int maxValue; + private final int maxIndex; + + public MapStack(long seed, + int size, + BiomePicker picker, + int mapHeight, + int worldHeight, + TriFunction mapConstructor) { + final int mapCount = Mth.ceil((float) worldHeight / mapHeight); + this.maxIndex = mapCount - 1; + this.worldHeight = worldHeight; + this.layerDistortion = mapHeight * 0.1; + minValue = Mth.floor(mapHeight * 0.5F + 0.5F); + maxValue = Mth.floor(worldHeight - mapHeight * 0.5F + 0.5F); + maps = new BiomeMap[mapCount]; + Random random = new Random(seed); + for (int i = 0; i < mapCount; i++) { + maps[i] = mapConstructor.apply(random.nextLong(), size, picker); + maps[i].setChunkProcessor(this::onChunkCreation); + } + noise = new OpenSimplexNoise(random.nextInt()); + } + + @Override + public void clearCache() { + for (BiomeMap map : maps) { + map.clearCache(); + } + } + + @Override + public void setChunkProcessor(TriConsumer processor) { + } + + @Override + public BiomeChunk getChunk(int cx, int cz, boolean update) { + return null; + } + + @Override + public BiomePicker.ActualBiome getBiome(double x, double y, double z) { + int mapIndex; + + if (y < minValue) { + mapIndex = 0; + } else if (y > maxValue) { + mapIndex = maxIndex; + } else { + mapIndex = Mth.floor((y + noise.eval(x * 0.03, + z * 0.03) * layerDistortion) / worldHeight * maxIndex + 0.5F); + mapIndex = Mth.clamp(mapIndex, 0, maxIndex); + } + + return maps[mapIndex].getBiome(x, y, z); + } + + private void onChunkCreation(int cx, int cz, int side) { + BiomePicker.ActualBiome[][] biomeMap = new BiomePicker.ActualBiome[side][side]; + BiomeChunk[] chunks = new BiomeChunk[maps.length]; + + boolean isNoEmpty = false; + for (int i = 0; i < maps.length; i++) { + chunks[i] = maps[i].getChunk(cx, cz, false); + for (int x = 0; x < side; x++) { + for (int z = 0; z < side; z++) { + if (biomeMap[x][z] == null) { + BiomePicker.ActualBiome biome = chunks[i].getBiome(x, z); + if (biome.bclBiome.isVertical()) { + biomeMap[x][z] = biome; + isNoEmpty = true; + } + } + } + } + } + + if (isNoEmpty) { + for (int i = 0; i < maps.length; i++) { + for (int x = 0; x < side; x++) { + for (int z = 0; z < side; z++) { + if (biomeMap[x][z] != null) { + chunks[i].setBiome(x, z, biomeMap[x][z]); + } + } + } + } + } + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/map/hex/HexBiomeChunk.java b/src/main/java/org/betterx/bclib/world/generator/map/hex/HexBiomeChunk.java new file mode 100644 index 00000000..beb2f199 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/map/hex/HexBiomeChunk.java @@ -0,0 +1,158 @@ +package org.betterx.bclib.world.generator.map.hex; + +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import org.betterx.bclib.interfaces.BiomeChunk; +import org.betterx.bclib.world.generator.BiomePicker; + +import java.util.Arrays; + +public class HexBiomeChunk implements BiomeChunk { + private static final short SIDE = 32; + private static final byte SIDE_PRE = 4; + private static final short SIZE = SIDE * SIDE; + private static final short MAX_SIDE = SIZE - SIDE; + private static final byte SCALE_PRE = SIDE / SIDE_PRE; + private static final byte SIZE_PRE = SIDE_PRE * SIDE_PRE; + private static final byte SIDE_MASK = SIDE - 1; + private static final byte SIDE_PRE_MASK = SIDE_PRE - 1; + private static final byte SIDE_OFFSET = (byte) Math.round(Math.log(SIDE) / Math.log(2)); + private static final byte SIDE_PRE_OFFSET = (byte) Math.round(Math.log(SIDE_PRE) / Math.log(2)); + private static final short[][] NEIGHBOURS; + + private final BiomePicker.ActualBiome[] biomes = new BiomePicker.ActualBiome[SIZE]; + + public HexBiomeChunk(WorldgenRandom random, BiomePicker picker) { + BiomePicker.ActualBiome[][] buffers = new BiomePicker.ActualBiome[2][SIZE]; + + for (BiomePicker.ActualBiome[] buffer : buffers) { + Arrays.fill(buffer, null); + } + + for (byte index = 0; index < SIZE_PRE; index++) { + byte px = (byte) (index >> SIDE_PRE_OFFSET); + byte pz = (byte) (index & SIDE_PRE_MASK); + px = (byte) (px * SCALE_PRE + random.nextInt(SCALE_PRE)); + pz = (byte) (pz * SCALE_PRE + random.nextInt(SCALE_PRE)); + circle(buffers[0], getIndex(px, pz), picker.getBiome(random), null); + } + + boolean hasEmptyCells = true; + byte bufferIndex = 0; + while (hasEmptyCells) { + BiomePicker.ActualBiome[] inBuffer = buffers[bufferIndex]; + bufferIndex = (byte) ((bufferIndex + 1) & 1); + BiomePicker.ActualBiome[] outBuffer = buffers[bufferIndex]; + hasEmptyCells = false; + + for (short index = SIDE; index < MAX_SIDE; index++) { + byte z = (byte) (index & SIDE_MASK); + if (z == 0 || z == SIDE_MASK) { + continue; + } + if (inBuffer[index] != null) { + outBuffer[index] = inBuffer[index]; + short[] neighbours = getNeighbours(index & SIDE_MASK); + short indexSide = (short) (index + neighbours[random.nextInt(6)]); + if (indexSide >= 0 && indexSide < SIZE && outBuffer[indexSide] == null) { + outBuffer[indexSide] = inBuffer[index]; + } + } else { + hasEmptyCells = true; + } + } + } + + BiomePicker.ActualBiome[] outBuffer = buffers[bufferIndex]; + byte preN = (byte) (SIDE_MASK - 2); + for (byte index = 0; index < SIDE; index++) { + outBuffer[getIndex(index, (byte) 0)] = outBuffer[getIndex(index, (byte) 2)]; + outBuffer[getIndex((byte) 0, index)] = outBuffer[getIndex((byte) 2, index)]; + outBuffer[getIndex(index, SIDE_MASK)] = outBuffer[getIndex(index, preN)]; + outBuffer[getIndex(SIDE_MASK, index)] = outBuffer[getIndex(preN, index)]; + } + + for (short index = 0; index < SIZE; index++) { + if (outBuffer[index] == null) { + outBuffer[index] = picker.getBiome(random); + } else if (random.nextInt(4) == 0) { + circle(outBuffer, index, outBuffer[index].getSubBiome(random), outBuffer[index]); + } + } + + System.arraycopy(outBuffer, 0, this.biomes, 0, SIZE); + } + + private void circle(BiomePicker.ActualBiome[] buffer, + short center, + BiomePicker.ActualBiome biome, + BiomePicker.ActualBiome mask) { + if (buffer[center] == mask) { + buffer[center] = biome; + } + short[] neighbours = getNeighbours(center & SIDE_MASK); + for (short i : neighbours) { + short index = (short) (center + i); + if (index >= 0 && index < SIZE && buffer[index] == mask) { + buffer[index] = biome; + } + } + } + + private static byte wrap(int value) { + return (byte) (value & SIDE_MASK); + } + + private short getIndex(byte x, byte z) { + return (short) ((short) x << SIDE_OFFSET | z); + } + + @Override + public BiomePicker.ActualBiome getBiome(int x, int z) { + return biomes[getIndex(wrap(x), wrap(z))]; + } + + @Override + public void setBiome(int x, int z, BiomePicker.ActualBiome biome) { + biomes[getIndex(wrap(x), wrap(z))] = biome; + } + + @Override + public int getSide() { + return SIDE; + } + + public static int scaleCoordinate(int value) { + return value >> SIDE_OFFSET; + } + + public static boolean isBorder(int value) { + return wrap(value) == SIDE_MASK; + } + + private short[] getNeighbours(int z) { + return NEIGHBOURS[z & 1]; + } + + public static float scaleMap(float size) { + return size / (SIDE >> 2); + } + + static { + NEIGHBOURS = new short[2][6]; + + NEIGHBOURS[0][0] = 1; + NEIGHBOURS[0][1] = -1; + NEIGHBOURS[0][2] = SIDE; + NEIGHBOURS[0][3] = -SIDE; + NEIGHBOURS[0][4] = SIDE + 1; + NEIGHBOURS[0][5] = SIDE - 1; + + NEIGHBOURS[1][0] = 1; + NEIGHBOURS[1][1] = -1; + NEIGHBOURS[1][2] = SIDE; + NEIGHBOURS[1][3] = -SIDE; + NEIGHBOURS[1][4] = -SIDE + 1; + NEIGHBOURS[1][5] = -SIDE - 1; + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/map/hex/HexBiomeMap.java b/src/main/java/org/betterx/bclib/world/generator/map/hex/HexBiomeMap.java new file mode 100644 index 00000000..dbdc6eb9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/map/hex/HexBiomeMap.java @@ -0,0 +1,185 @@ +package org.betterx.bclib.world.generator.map.hex; + +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import com.google.common.collect.Maps; +import org.betterx.bclib.interfaces.BiomeChunk; +import org.betterx.bclib.interfaces.BiomeMap; +import org.betterx.bclib.interfaces.TriConsumer; +import org.betterx.bclib.noise.OpenSimplexNoise; +import org.betterx.bclib.util.MHelper; +import org.betterx.bclib.world.generator.BiomePicker; + +import java.util.Map; +import java.util.Random; + +public class HexBiomeMap implements BiomeMap { + private static final float RAD_INNER = (float) Math.sqrt(3.0) * 0.5F; + private static final float COEF = 0.25F * (float) Math.sqrt(3.0); + private static final float COEF_HALF = COEF * 0.5F; + private static final float SIN = (float) Math.sin(0.4); + private static final float COS = (float) Math.cos(0.4); + private static final float[] EDGE_CIRCLE_X; + private static final float[] EDGE_CIRCLE_Z; + + private final Map chunks = Maps.newConcurrentMap(); + private final BiomePicker picker; + + private final OpenSimplexNoise[] noises = new OpenSimplexNoise[2]; + private TriConsumer processor; + private final byte noiseIterations; + private final float scale; + private final int seed; + + public HexBiomeMap(long seed, int size, BiomePicker picker) { + this.picker = picker; + this.scale = HexBiomeChunk.scaleMap(size); + Random random = new Random(seed); + noises[0] = new OpenSimplexNoise(random.nextInt()); + noises[1] = new OpenSimplexNoise(random.nextInt()); + noiseIterations = (byte) Math.min(Math.ceil(Math.log(scale) / Math.log(2)), 5); + this.seed = random.nextInt(); + } + + @Override + public void clearCache() { + if (chunks.size() > 127) { + chunks.clear(); + } + } + + @Override + public BiomePicker.ActualBiome getBiome(double x, double y, double z) { + BiomePicker.ActualBiome biome = getRawBiome(x, z); + BiomePicker.ActualBiome edge = biome.getEdge(); + int size = biome.bclBiome.getEdgeSize(); + + if (edge == null && biome.getParentBiome() != null) { + edge = biome.getParentBiome().getEdge(); + size = biome.getParentBiome().bclBiome.getEdgeSize(); + } + + if (edge == null) { + return biome; + } + + for (byte i = 0; i < 8; i++) { + if (!getRawBiome(x + size * EDGE_CIRCLE_X[i], z + size * EDGE_CIRCLE_Z[i]).isSame(biome)) { + return edge; + } + } + + return biome; + } + + @Override + public BiomeChunk getChunk(final int cx, final int cz, final boolean update) { + final ChunkPos pos = new ChunkPos(cx, cz); + HexBiomeChunk chunk = chunks.get(pos); + if (chunk == null) { + WorldgenRandom random = new WorldgenRandom(RandomSource.create(MHelper.getSeed(seed, cx, cz))); + chunk = new HexBiomeChunk(random, picker); + if (update && processor != null) { + processor.accept(cx, cz, chunk.getSide()); + } + chunks.put(pos, chunk); + } + return chunk; + } + + @Override + public void setChunkProcessor(TriConsumer processor) { + this.processor = processor; + } + + private BiomePicker.ActualBiome getRawBiome(double x, double z) { + double px = x / scale * RAD_INNER; + double pz = z / scale; + double dx = rotateX(px, pz); + double dz = rotateZ(px, pz); + px = dx; + pz = dz; + + dx = getNoise(px, pz, (byte) 0) * 0.2F; + dz = getNoise(pz, px, (byte) 1) * 0.2F; + px += dx; + pz += dz; + + int cellZ = (int) Math.floor(pz); + boolean offset = (cellZ & 1) == 1; + + if (offset) { + px += 0.5; + } + + int cellX = (int) Math.floor(px); + + float pointX = (float) (px - cellX - 0.5); + float pointZ = (float) (pz - cellZ - 0.5); + + if (Math.abs(pointZ) < 0.3333F) { + return getChunkBiome(cellX, cellZ); + } + + if (insideHexagon(0, 0, 1.1555F, pointZ * RAD_INNER, pointX)) { + return getChunkBiome(cellX, cellZ); + } + + cellX = pointX < 0 ? (offset ? cellX - 1 : cellX) : (offset ? cellX : cellX + 1); + cellZ = pointZ < 0 ? cellZ - 1 : cellZ + 1; + + return getChunkBiome(cellX, cellZ); + } + + private BiomePicker.ActualBiome getChunkBiome(int x, int z) { + int cx = HexBiomeChunk.scaleCoordinate(x); + int cz = HexBiomeChunk.scaleCoordinate(z); + + if (((z >> 2) & 1) == 0 && HexBiomeChunk.isBorder(x)) { + x = 0; + cx += 1; + } else if (((x >> 2) & 1) == 0 && HexBiomeChunk.isBorder(z)) { + z = 0; + cz += 1; + } + + return getChunk(cx, cz, true).getBiome(x, z); + } + + private boolean insideHexagon(float centerX, float centerZ, float radius, float x, float z) { + double dx = Math.abs(x - centerX) / radius; + double dy = Math.abs(z - centerZ) / radius; + return (dy <= COEF) && (COEF * dx + 0.25F * dy <= COEF_HALF); + } + + private double getNoise(double x, double z, byte state) { + double result = 0; + for (byte i = 1; i <= noiseIterations; i++) { + OpenSimplexNoise noise = noises[state]; + state = (byte) ((state + 1) & 1); + result += noise.eval(x * i, z * i) / i; + } + return result; + } + + private double rotateX(double x, double z) { + return x * COS - z * SIN; + } + + private double rotateZ(double x, double z) { + return x * SIN + z * COS; + } + + static { + EDGE_CIRCLE_X = new float[8]; + EDGE_CIRCLE_Z = new float[8]; + + for (byte i = 0; i < 8; i++) { + float angle = i / 4F * (float) Math.PI; + EDGE_CIRCLE_X[i] = (float) Math.sin(angle); + EDGE_CIRCLE_Z[i] = (float) Math.cos(angle); + } + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/map/square/SquareBiomeChunk.java b/src/main/java/org/betterx/bclib/world/generator/map/square/SquareBiomeChunk.java new file mode 100644 index 00000000..1747e236 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/map/square/SquareBiomeChunk.java @@ -0,0 +1,66 @@ +package org.betterx.bclib.world.generator.map.square; + +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import org.betterx.bclib.interfaces.BiomeChunk; +import org.betterx.bclib.world.generator.BiomePicker; + +public class SquareBiomeChunk implements BiomeChunk { + private static final int BIT_OFFSET = 4; + protected static final int WIDTH = 1 << BIT_OFFSET; + private static final int SM_WIDTH = WIDTH >> 1; + private static final int SM_BIT_OFFSET = BIT_OFFSET >> 1; + private static final int MASK_OFFSET = SM_WIDTH - 1; + protected static final int MASK_WIDTH = WIDTH - 1; + + private static final int SM_CAPACITY = SM_WIDTH * SM_WIDTH; + private static final int CAPACITY = WIDTH * WIDTH; + + private final BiomePicker.ActualBiome[] biomes; + + public SquareBiomeChunk(WorldgenRandom random, BiomePicker picker) { + BiomePicker.ActualBiome[] PreBio = new BiomePicker.ActualBiome[SM_CAPACITY]; + biomes = new BiomePicker.ActualBiome[CAPACITY]; + + for (int x = 0; x < SM_WIDTH; x++) { + int offset = x << SM_BIT_OFFSET; + for (int z = 0; z < SM_WIDTH; z++) { + PreBio[offset | z] = picker.getBiome(random); + } + } + + for (int x = 0; x < WIDTH; x++) { + int offset = x << BIT_OFFSET; + for (int z = 0; z < WIDTH; z++) { + biomes[offset | z] = PreBio[getSmIndex(offsetXZ(x, random), offsetXZ(z, random))].getSubBiome(random); + } + } + } + + @Override + public BiomePicker.ActualBiome getBiome(int x, int z) { + return biomes[getIndex(x & MASK_WIDTH, z & MASK_WIDTH)]; + } + + @Override + public void setBiome(int x, int z, BiomePicker.ActualBiome biome) { + biomes[getIndex(x & MASK_WIDTH, z & MASK_WIDTH)] = biome; + } + + @Override + public int getSide() { + return WIDTH; + } + + private int offsetXZ(int x, WorldgenRandom random) { + return ((x + random.nextInt(2)) >> 1) & MASK_OFFSET; + } + + private int getIndex(int x, int z) { + return x << BIT_OFFSET | z; + } + + private int getSmIndex(int x, int z) { + return x << SM_BIT_OFFSET | z; + } +} diff --git a/src/main/java/org/betterx/bclib/world/generator/map/square/SquareBiomeMap.java b/src/main/java/org/betterx/bclib/world/generator/map/square/SquareBiomeMap.java new file mode 100644 index 00000000..18d4868e --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/generator/map/square/SquareBiomeMap.java @@ -0,0 +1,140 @@ +package org.betterx.bclib.world.generator.map.square; + +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.levelgen.LegacyRandomSource; +import net.minecraft.world.level.levelgen.WorldgenRandom; + +import com.google.common.collect.Maps; +import org.betterx.bclib.interfaces.BiomeChunk; +import org.betterx.bclib.interfaces.BiomeMap; +import org.betterx.bclib.interfaces.TriConsumer; +import org.betterx.bclib.noise.OpenSimplexNoise; +import org.betterx.bclib.util.MHelper; +import org.betterx.bclib.world.generator.BiomePicker; + +import java.util.Map; + +public class SquareBiomeMap implements BiomeMap { + private final Map maps = Maps.newHashMap(); + private final OpenSimplexNoise noiseX; + private final OpenSimplexNoise noiseZ; + private final WorldgenRandom random; + private final BiomePicker picker; + + private final int sizeXZ; + private final int depth; + private final int size; + + private TriConsumer processor; + + public SquareBiomeMap(long seed, int size, BiomePicker picker) { + random = new WorldgenRandom(new LegacyRandomSource(seed)); + noiseX = new OpenSimplexNoise(random.nextLong()); + noiseZ = new OpenSimplexNoise(random.nextLong()); + this.sizeXZ = size; + depth = (int) Math.ceil(Math.log(size) / Math.log(2)) - 2; + this.size = 1 << depth; + this.picker = picker; + } + + @Override + public void clearCache() { + if (maps.size() > 32) { + maps.clear(); + } + } + + @Override + public BiomePicker.ActualBiome getBiome(double x, double y, double z) { + BiomePicker.ActualBiome biome = getRawBiome(x, z); + + if (biome.getEdge() != null || (biome.getParentBiome() != null && biome.getParentBiome().getEdge() != null)) { + BiomePicker.ActualBiome search = biome; + if (biome.getParentBiome() != null) { + search = biome.getParentBiome(); + } + + int size = search.bclBiome.getEdgeSize(); + boolean edge = !search.isSame(getRawBiome(x + size, z)); + edge = edge || !search.isSame(getRawBiome(x - size, z)); + edge = edge || !search.isSame(getRawBiome(x, z + size)); + edge = edge || !search.isSame(getRawBiome(x, z - size)); + edge = edge || !search.isSame(getRawBiome(x - 1, z - 1)); + edge = edge || !search.isSame(getRawBiome(x - 1, z + 1)); + edge = edge || !search.isSame(getRawBiome(x + 1, z - 1)); + edge = edge || !search.isSame(getRawBiome(x + 1, z + 1)); + + if (edge) { + biome = search.getEdge(); + } + } + + return biome; + } + + @Override + public void setChunkProcessor(TriConsumer processor) { + this.processor = processor; + } + + @Override + public BiomeChunk getChunk(int cx, int cz, boolean update) { + ChunkPos cpos = new ChunkPos(cx, cz); + SquareBiomeChunk chunk = maps.get(cpos); + if (chunk == null) { + synchronized (random) { + random.setLargeFeatureWithSalt(0, cpos.x, cpos.z, 0); + chunk = new SquareBiomeChunk(random, picker); + } + maps.put(cpos, chunk); + + if (update && processor != null) { + processor.accept(cx, cz, chunk.getSide()); + } + } + + return chunk; + } + + private BiomePicker.ActualBiome getRawBiome(double bx, double bz) { + double x = bx * size / sizeXZ; + double z = bz * size / sizeXZ; + + double px = bx * 0.2; + double pz = bz * 0.2; + + for (int i = 0; i < depth; i++) { + double nx = (x + noiseX.eval(px, pz)) / 2F; + double nz = (z + noiseZ.eval(px, pz)) / 2F; + + x = nx; + z = nz; + + px = px / 2 + i; + pz = pz / 2 + i; + } + + int ix = MHelper.floor(x); + int iz = MHelper.floor(z); + + if ((ix & SquareBiomeChunk.MASK_WIDTH) == SquareBiomeChunk.MASK_WIDTH) { + x += (iz / 2) & 1; + } + if ((iz & SquareBiomeChunk.MASK_WIDTH) == SquareBiomeChunk.MASK_WIDTH) { + z += (ix / 2) & 1; + } + + ChunkPos cpos = new ChunkPos(MHelper.floor(x / SquareBiomeChunk.WIDTH), + MHelper.floor(z / SquareBiomeChunk.WIDTH)); + SquareBiomeChunk chunk = maps.get(cpos); + if (chunk == null) { + synchronized (random) { + random.setLargeFeatureWithSalt(0, cpos.x, cpos.z, 0); + chunk = new SquareBiomeChunk(random, picker); + } + maps.put(cpos, chunk); + } + + return chunk.getBiome(MHelper.floor(x), MHelper.floor(z)); + } +} diff --git a/src/main/java/org/betterx/bclib/world/processors/DestructionStructureProcessor.java b/src/main/java/org/betterx/bclib/world/processors/DestructionStructureProcessor.java new file mode 100644 index 00000000..9592e8c8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/processors/DestructionStructureProcessor.java @@ -0,0 +1,41 @@ +package org.betterx.bclib.world.processors; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; + +import org.betterx.bclib.util.BlocksHelper; +import org.betterx.bclib.util.MHelper; + +public class DestructionStructureProcessor extends StructureProcessor { + private int chance = 4; + + public void setChance(int chance) { + this.chance = chance; + } + + @Override + public StructureBlockInfo processBlock(LevelReader worldView, + BlockPos pos, + BlockPos blockPos, + StructureBlockInfo structureBlockInfo, + StructureBlockInfo structureBlockInfo2, + StructurePlaceSettings structurePlacementData) { + if (!BlocksHelper.isInvulnerable( + structureBlockInfo2.state, + worldView, + structureBlockInfo2.pos + ) && MHelper.RANDOM.nextInt(chance) == 0) { + return null; + } + return structureBlockInfo2; + } + + @Override + protected StructureProcessorType getType() { + return StructureProcessorType.RULE; + } +} diff --git a/src/main/java/org/betterx/bclib/world/processors/TerrainStructureProcessor.java b/src/main/java/org/betterx/bclib/world/processors/TerrainStructureProcessor.java new file mode 100644 index 00000000..b453a194 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/processors/TerrainStructureProcessor.java @@ -0,0 +1,41 @@ +package org.betterx.bclib.world.processors; + +import net.minecraft.core.BlockPos; +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.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; + +import org.betterx.bclib.api.biomes.BiomeAPI; + +public class TerrainStructureProcessor extends StructureProcessor { + private final Block defaultBlock; + + public TerrainStructureProcessor(Block defaultBlock) { + this.defaultBlock = defaultBlock; + } + + @Override + public StructureBlockInfo processBlock(LevelReader worldView, + BlockPos pos, + BlockPos blockPos, + StructureBlockInfo structureBlockInfo, + StructureBlockInfo structureBlockInfo2, + StructurePlaceSettings structurePlacementData) { + BlockPos bpos = structureBlockInfo2.pos; + if (structureBlockInfo2.state.is(defaultBlock) && worldView.isEmptyBlock(bpos.above())) { + final BlockState top = BiomeAPI.findTopMaterial(worldView.getBiome(pos)) + .orElse(defaultBlock.defaultBlockState()); + return new StructureBlockInfo(bpos, top, structureBlockInfo2.nbt); + } + return structureBlockInfo2; + } + + @Override + protected StructureProcessorType getType() { + return StructureProcessorType.RULE; + } +} diff --git a/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java b/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java new file mode 100644 index 00000000..9f74e0df --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java @@ -0,0 +1,178 @@ +package org.betterx.bclib.world.structures; + +import net.minecraft.core.*; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.data.worldgen.StructureSets; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.structure.*; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType; + +import com.google.common.collect.Lists; +import com.mojang.serialization.Codec; +import org.betterx.bclib.api.biomes.BCLBiomeBuilder; +import org.betterx.bclib.api.tag.TagAPI; +import org.betterx.bclib.mixin.common.StructuresAccessor; + +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.function.Function; + +public class BCLStructure { + private static final Random RANDOM = new Random(354); + + private final Holder structure; + private final GenerationStep.Decoration featureStep; + private final List biomes = Lists.newArrayList(); + private final ResourceLocation id; + public final TagKey biomeTag; + public final ResourceKey structureKey; + public final S baseStructure; + public final ResourceKey structureSetKey; + public final RandomSpreadStructurePlacement spreadConfig; + + public final StructureType structureType; + + public final Codec STRUCTURE_CODEC; + + + private static HolderSet biomes(TagKey tagKey) { + return BuiltinRegistries.BIOME.getOrCreateTag(tagKey); + } + + private static Structure.StructureSettings structure(TagKey tagKey, + Map map, + GenerationStep.Decoration decoration, + TerrainAdjustment terrainAdjustment) { + return new Structure.StructureSettings(biomes(tagKey), map, decoration, terrainAdjustment); + } + + private static Structure.StructureSettings structure(TagKey tagKey, + GenerationStep.Decoration decoration, + TerrainAdjustment terrainAdjustment) { + return structure(tagKey, Map.of(), decoration, terrainAdjustment); + } + + private static StructureType registerStructureType(ResourceLocation id, Codec codec) { + return Registry.register(Registry.STRUCTURE_TYPES, id, () -> codec); + } + + public BCLStructure(ResourceLocation id, + Function structureBuilder, + GenerationStep.Decoration step, + int spacing, + int separation) { + this(id, structureBuilder, step, spacing, separation, false); + } + + public BCLStructure(ResourceLocation id, + Function structureBuilder, + GenerationStep.Decoration step, + int spacing, + int separation, + boolean adaptNoise) { + this.id = id; + this.featureStep = step; + + this.STRUCTURE_CODEC = Structure.simpleCodec(structureBuilder); + //parts from vanilla for Structure generation + //public static final ResourceKey> JUNGLE_TEMPLE = + // BuiltinStructures.createKey("jungle_pyramid"); + //public static final Holder> JUNGLE_TEMPLE = + // Structures.register(BuiltinStructures.JUNGLE_TEMPLE, Structure.JUNGLE_TEMPLE.configured(NoneFeatureConfiguration.INSTANCE, BiomeTags.HAS_JUNGLE_TEMPLE)); + //public static final Holder JUNGLE_TEMPLES = + // StructureSets.register(BuiltinStructureSets.JUNGLE_TEMPLES, Structures.JUNGLE_TEMPLE, new RandomSpreadStructurePlacement(32, 8, RandomSpreadType.LINEAR, 14357619)); + //public static final Structure JUNGLE_TEMPLE = + // Structure.register("jungle_pyramid", new JunglePyramidFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES); + // + + this.spreadConfig = new RandomSpreadStructurePlacement(spacing, + separation, + RandomSpreadType.LINEAR, + RANDOM.nextInt(8192)); + this.structureKey = ResourceKey.create(Registry.STRUCTURE_REGISTRY, id); + this.structureSetKey = ResourceKey.create(Registry.STRUCTURE_SET_REGISTRY, id); + this.structureType = registerStructureType(id, STRUCTURE_CODEC); + + this.biomeTag = TagAPI.makeBiomeTag(id.getNamespace(), "has_structure/" + id.getPath()); + this.baseStructure = structureBuilder.apply(structure(biomeTag, featureStep, TerrainAdjustment.NONE)); + this.structure = StructuresAccessor.callRegister(structureKey, this.baseStructure); + StructureSets.register(structureSetKey, this.structure, spreadConfig); + } + + /** + * runs the {@code PieceGeneratorSupplier.Context::validBiome} from the given context at + * height=5 in the middle of the chunk. + * + * @param context The context to test with. + * @return true, if this feature can spawn in the current biome + */ + public static boolean isValidBiome(Structure.GenerationContext context) { + return isValidBiome(context, 5); + } + + /** + * runs the {@code PieceGeneratorSupplier.Context::validBiome} from the given context at the + * given height in the middle of the chunk. + * + * @param context The context to test with. + * @param yPos The Height to test for + * @return true, if this feature can spawn in the current biome + */ + public static boolean isValidBiome(Structure.GenerationContext context, int yPos) { + BlockPos blockPos = context.chunkPos().getMiddleBlockPosition(yPos); + return context.validBiome().test( + context + .chunkGenerator() + .getBiomeSource() + .getNoiseBiome( + QuartPos.fromBlock(blockPos.getX()), + QuartPos.fromBlock(blockPos.getY()), + QuartPos.fromBlock(blockPos.getZ()), + context.randomState().sampler() + ) + ); + } + + public Holder getStructure() { + return structure; + } + + public GenerationStep.Decoration getFeatureStep() { + return featureStep; + } + + /** + * Get the structure ID; + * + * @return {@link ResourceLocation} id. + */ + public ResourceLocation getID() { + return id; + } + + /** + * Adds biome into internal biome list, used in {@link BCLBiomeBuilder}. + * + * @param biome {@link ResourceLocation} biome ID. + */ + public void addInternalBiome(ResourceLocation biome) { + biomes.add(biome); + } + + /** + * Get biome list where this structure feature can generate. Only represents biomes made with {@link BCLBiomeBuilder} and only + * if structure was added during building process. Modification of this list will not affect structure generation. + * + * @return {@link List} of biome {@link ResourceLocation}. + */ + public List getBiomes() { + return biomes; + } +} diff --git a/src/main/java/org/betterx/bclib/world/structures/StructureWorld.java b/src/main/java/org/betterx/bclib/world/structures/StructureWorld.java new file mode 100644 index 00000000..59d36126 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/structures/StructureWorld.java @@ -0,0 +1,172 @@ +package org.betterx.bclib.world.structures; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.structure.BoundingBox; + +import com.google.common.collect.Maps; + +import java.util.Map; + +public class StructureWorld { + private final Map parts = Maps.newHashMap(); + private ChunkPos lastPos; + private Part lastPart; + private int minX = Integer.MAX_VALUE; + private int minY = Integer.MAX_VALUE; + private int minZ = Integer.MAX_VALUE; + private int maxX = Integer.MIN_VALUE; + private int maxY = Integer.MIN_VALUE; + private int maxZ = Integer.MIN_VALUE; + + public StructureWorld() { + } + + public StructureWorld(CompoundTag tag) { + minX = tag.getInt("minX"); + maxX = tag.getInt("maxX"); + minY = tag.getInt("minY"); + maxY = tag.getInt("maxY"); + minZ = tag.getInt("minZ"); + maxZ = tag.getInt("maxZ"); + + ListTag map = tag.getList("parts", 10); + map.forEach((element) -> { + CompoundTag compound = (CompoundTag) element; + Part part = new Part(compound); + int x = compound.getInt("x"); + int z = compound.getInt("z"); + parts.put(new ChunkPos(x, z), part); + }); + } + + public void setBlock(BlockPos pos, BlockState state) { + ChunkPos cPos = new ChunkPos(pos); + + if (cPos.equals(lastPos)) { + lastPart.addBlock(pos, state); + return; + } + + Part part = parts.get(cPos); + if (part == null) { + part = new Part(); + parts.put(cPos, part); + + if (cPos.x < minX) minX = cPos.x; + if (cPos.x > maxX) maxX = cPos.x; + if (cPos.z < minZ) minZ = cPos.z; + if (cPos.z > maxZ) maxZ = cPos.z; + } + if (pos.getY() < minY) minY = pos.getY(); + if (pos.getY() > maxY) maxY = pos.getY(); + part.addBlock(pos, state); + + lastPos = cPos; + lastPart = part; + } + + public boolean placeChunk(WorldGenLevel world, ChunkPos chunkPos) { + Part part = parts.get(chunkPos); + if (part != null) { + ChunkAccess chunk = world.getChunk(chunkPos.x, chunkPos.z); + part.placeChunk(chunk); + return true; + } + return false; + } + + public CompoundTag toBNT() { + CompoundTag tag = new CompoundTag(); + tag.putInt("minX", minX); + tag.putInt("maxX", maxX); + tag.putInt("minY", minY); + tag.putInt("maxY", maxY); + tag.putInt("minZ", minZ); + tag.putInt("maxZ", maxZ); + ListTag map = new ListTag(); + tag.put("parts", map); + parts.forEach((pos, part) -> { + map.add(part.toNBT(pos.x, pos.z)); + }); + return tag; + } + + public BoundingBox getBounds() { + if (minX == Integer.MAX_VALUE || maxX == Integer.MIN_VALUE || minZ == Integer.MAX_VALUE || maxZ == Integer.MIN_VALUE) { + return BoundingBox.infinite(); + } + return new BoundingBox(minX << 4, minY, minZ << 4, (maxX << 4) | 15, maxY, (maxZ << 4) | 15); + } + + private static final class Part { + Map blocks = Maps.newHashMap(); + + public Part() { + } + + public Part(CompoundTag tag) { + ListTag map = tag.getList("blocks", 10); + ListTag map2 = tag.getList("states", 10); + BlockState[] states = new BlockState[map2.size()]; + for (int i = 0; i < states.length; i++) { + states[i] = NbtUtils.readBlockState((CompoundTag) map2.get(i)); + } + + map.forEach((element) -> { + CompoundTag block = (CompoundTag) element; + BlockPos pos = NbtUtils.readBlockPos(block.getCompound("pos")); + int stateID = block.getInt("state"); + BlockState state = stateID < states.length ? states[stateID] : Block.stateById(stateID); + blocks.put(pos, state); + }); + } + + void addBlock(BlockPos pos, BlockState state) { + BlockPos inner = new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15); + blocks.put(inner, state); + } + + void placeChunk(ChunkAccess chunk) { + blocks.forEach((pos, state) -> { + chunk.setBlockState(pos, state, false); + }); + } + + CompoundTag toNBT(int x, int z) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", x); + tag.putInt("z", z); + ListTag map = new ListTag(); + tag.put("blocks", map); + ListTag stateMap = new ListTag(); + tag.put("states", stateMap); + + int[] id = new int[1]; + Map states = Maps.newHashMap(); + + blocks.forEach((pos, state) -> { + int stateID = states.getOrDefault(states, -1); + if (stateID < 0) { + stateID = id[0]++; + states.put(state, stateID); + stateMap.add(NbtUtils.writeBlockState(state)); + } + + CompoundTag block = new CompoundTag(); + block.put("pos", NbtUtils.writeBlockPos(pos)); + block.putInt("state", stateID); + map.add(block); + }); + + return tag; + } + } +} diff --git a/src/main/java/org/betterx/bclib/world/surface/DoubleBlockSurfaceNoiseCondition.java b/src/main/java/org/betterx/bclib/world/surface/DoubleBlockSurfaceNoiseCondition.java new file mode 100644 index 00000000..30b63787 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/surface/DoubleBlockSurfaceNoiseCondition.java @@ -0,0 +1,53 @@ +package org.betterx.bclib.world.surface; + +import net.minecraft.core.Registry; +import net.minecraft.util.KeyDispatchDataCodec; +import net.minecraft.world.level.levelgen.SurfaceRules; + +import com.mojang.serialization.Codec; +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.surface.rules.SurfaceNoiseCondition; +import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor; +import org.betterx.bclib.noise.OpenSimplexNoise; +import org.betterx.bclib.util.MHelper; + +public class DoubleBlockSurfaceNoiseCondition extends SurfaceNoiseCondition { + public static final DoubleBlockSurfaceNoiseCondition CONDITION = new DoubleBlockSurfaceNoiseCondition(0); + private static final OpenSimplexNoise NOISE = new OpenSimplexNoise(4141); + public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(Codec.DOUBLE.fieldOf( + "threshold").xmap(DoubleBlockSurfaceNoiseCondition::new, obj -> obj.threshold).codec()); + private final double threshold; + + public DoubleBlockSurfaceNoiseCondition(double threshold) { + this.threshold = threshold; + } + + @Override + public KeyDispatchDataCodec codec() { + return CODEC; + } + + private static int lastX = Integer.MIN_VALUE; + private static int lastZ = Integer.MIN_VALUE; + private static double lastValue = 0; + + @Override + public boolean test(SurfaceRulesContextAccessor context) { + final int x = context.getBlockX(); + final int z = context.getBlockZ(); + if (lastX == x && lastZ == z) return lastValue > threshold; + + double value = NOISE.eval(x * 0.1, z * 0.1) + MHelper.randRange(-0.4, 0.4, MHelper.RANDOM_SOURCE); + + lastX = x; + lastZ = z; + lastValue = value; + return value > threshold; + } + + static { + Registry.register(Registry.CONDITION, + BCLib.makeID("doubleblock_surface"), + DoubleBlockSurfaceNoiseCondition.CODEC.codec()); + } +} diff --git a/src/main/java/ru/bclib/BCLib.java b/src/main/java/ru/bclib/BCLib.java deleted file mode 100644 index afc2ace1..00000000 --- a/src/main/java/ru/bclib/BCLib.java +++ /dev/null @@ -1,67 +0,0 @@ -package ru.bclib; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.api.WorldDataAPI; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.dataexchange.handler.autosync.Chunker; -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.tag.TagAPI; -import ru.bclib.config.Configs; -import ru.bclib.recipes.AnvilRecipe; -import ru.bclib.recipes.CraftingRecipes; -import ru.bclib.registry.BaseBlockEntities; -import ru.bclib.registry.BaseRegistry; -import ru.bclib.util.Logger; -import ru.bclib.world.generator.BCLibEndBiomeSource; -import ru.bclib.world.generator.BCLibNetherBiomeSource; -import ru.bclib.world.generator.GeneratorOptions; - -import java.util.List; - -public class BCLib implements ModInitializer { - public static final String MOD_ID = "bclib"; - public static final Logger LOGGER = new Logger(MOD_ID); - - @Override - public void onInitialize() { - BaseRegistry.register(); - GeneratorOptions.init(); - BaseBlockEntities.register(); - BCLibEndBiomeSource.register(); - BCLibNetherBiomeSource.register(); - TagAPI.init(); - CraftingRecipes.init(); - WorldDataAPI.registerModCache(MOD_ID); - DataExchangeAPI.registerMod(MOD_ID); - AnvilRecipe.register(); - - DataExchangeAPI.registerDescriptors(List.of( - HelloClient.DESCRIPTOR, - HelloServer.DESCRIPTOR, - RequestFiles.DESCRIPTOR, - SendFiles.DESCRIPTOR, - Chunker.DESCRIPTOR - )); - - BCLibPatch.register(); - Configs.save(); - } - - public static boolean isDevEnvironment() { - return FabricLoader.getInstance().isDevelopmentEnvironment(); - } - - public static boolean isClient() { - return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT; - } - - public static ResourceLocation makeID(String path) { - return new ResourceLocation(MOD_ID, path); - } -} diff --git a/src/main/java/ru/bclib/BCLibPatch.java b/src/main/java/ru/bclib/BCLibPatch.java deleted file mode 100644 index 63e31bf4..00000000 --- a/src/main/java/ru/bclib/BCLibPatch.java +++ /dev/null @@ -1,94 +0,0 @@ -package ru.bclib; - -import net.minecraft.nbt.CompoundTag; -import ru.bclib.api.datafixer.DataFixerAPI; -import ru.bclib.api.datafixer.ForcedLevelPatch; -import ru.bclib.api.datafixer.MigrationProfile; -import ru.bclib.config.Configs; -import ru.bclib.world.generator.GeneratorOptions; - -public final class BCLibPatch { - public static void register() { - // TODO separate values in config on client side (config screen) - if (Configs.MAIN_CONFIG.repairBiomes() && (GeneratorOptions.fixEndBiomeSource() || GeneratorOptions.fixNetherBiomeSource())) { - DataFixerAPI.registerPatch(BiomeSourcePatch::new); - } - } -} - -final class BiomeSourcePatch extends ForcedLevelPatch{ - private static final String NETHER_BIOME_SOURCE = "bclib:nether_biome_source"; - private static final String END_BIOME_SOURCE = "bclib:end_biome_source"; - private static final String MC_NETHER = "minecraft:the_nether"; - private static final String MC_END = "minecraft:the_end"; - - protected BiomeSourcePatch() { - super(BCLib.MOD_ID, "1.2.1"); - } - - @Override - protected Boolean runLevelDatPatch(CompoundTag root, MigrationProfile profile) { - CompoundTag worldGenSettings = root.getCompound("Data").getCompound("WorldGenSettings"); - CompoundTag dimensions = worldGenSettings.getCompound("dimensions"); - long seed = worldGenSettings.getLong("seed"); - boolean result = false; - - if (GeneratorOptions.fixNetherBiomeSource()) { - if (!dimensions.contains(MC_NETHER) || !isBCLibEntry(dimensions.getCompound(MC_NETHER))) { - CompoundTag dimRoot = new CompoundTag(); - dimRoot.put("generator", makeNetherGenerator(seed)); - dimRoot.putString("type", MC_NETHER); - dimensions.put(MC_NETHER, dimRoot); - result = true; - } - } - - if (GeneratorOptions.fixEndBiomeSource()) { - if (!dimensions.contains(MC_END) || !isBCLibEntry(dimensions.getCompound(MC_END))) { - CompoundTag dimRoot = new CompoundTag(); - dimRoot.put("generator", makeEndGenerator(seed)); - dimRoot.putString("type", MC_END); - dimensions.put(MC_END, dimRoot); - result = true; - } - } - - return result; - } - - private boolean isBCLibEntry(CompoundTag dimRoot) { - String type = dimRoot.getCompound("generator").getCompound("biome_source").getString("type"); - if (type.isEmpty() || type.length() < 5) { - return false; - } - return type.startsWith("bclib"); - } - - public static CompoundTag makeNetherGenerator(long seed) { - CompoundTag generator = new CompoundTag(); - generator.putString("type", "minecraft:noise"); - generator.putString("settings", "minecraft:nether"); - generator.putLong("seed", seed); - - CompoundTag biomeSource = new CompoundTag(); - biomeSource.putString("type", NETHER_BIOME_SOURCE); - biomeSource.putLong("seed", seed); - generator.put("biome_source", biomeSource); - - return generator; - } - - public static CompoundTag makeEndGenerator(long seed) { - CompoundTag generator = new CompoundTag(); - generator.putString("type", "minecraft:noise"); - generator.putString("settings", "minecraft:end"); - generator.putLong("seed", seed); - - CompoundTag biomeSource = new CompoundTag(); - biomeSource.putString("type", END_BIOME_SOURCE); - biomeSource.putLong("seed", seed); - generator.put("biome_source", biomeSource); - - return generator; - } -} diff --git a/src/main/java/ru/bclib/api/BonemealAPI.java b/src/main/java/ru/bclib/api/BonemealAPI.java deleted file mode 100644 index 101930c0..00000000 --- a/src/main/java/ru/bclib/api/BonemealAPI.java +++ /dev/null @@ -1,143 +0,0 @@ -package ru.bclib.api; - -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import ru.bclib.util.WeightedList; - -import java.util.Map; -import java.util.Random;import net.minecraft.util.RandomSource; -import java.util.Set; - -public class BonemealAPI { - private static final Map>> WATER_GRASS_BIOMES = Maps.newHashMap(); - private static final Map>> LAND_GRASS_BIOMES = Maps.newHashMap(); - private static final Map> WATER_GRASS_TYPES = Maps.newHashMap(); - private static final Map> LAND_GRASS_TYPES = Maps.newHashMap(); - private static final Map SPREADABLE_BLOCKS = Maps.newHashMap(); - private static final Set TERRAIN_TO_SPREAD = Sets.newHashSet(); - private static final Set TERRAIN = Sets.newHashSet(); - - public static void addSpreadableBlock(Block spreadableBlock, Block surfaceForSpread) { - SPREADABLE_BLOCKS.put(spreadableBlock, surfaceForSpread); - TERRAIN_TO_SPREAD.add(surfaceForSpread); - TERRAIN.add(surfaceForSpread); - } - - public static boolean isTerrain(Block block) { - return TERRAIN.contains(block); - } - - public static boolean isSpreadableTerrain(Block block) { - return TERRAIN_TO_SPREAD.contains(block); - } - - public static Block getSpreadable(Block block) { - return SPREADABLE_BLOCKS.get(block); - } - - public static void addLandGrass(Block plant, Block... terrain) { - for (Block block : terrain) { - addLandGrass(plant, block, 1F); - } - } - - public static void addLandGrass(ResourceLocation biome, Block plant, Block... terrain) { - for (Block block : terrain) { - addLandGrass(biome, plant, block, 1F); - } - } - - public static void addLandGrass(Block plant, Block terrain, float chance) { - WeightedList list = LAND_GRASS_TYPES.get(terrain); - if (list == null) { - list = new WeightedList(); - LAND_GRASS_TYPES.put(terrain, list); - } - TERRAIN.add(terrain); - list.add(plant, chance); - } - - public static void addLandGrass(ResourceLocation biome, Block plant, Block terrain, float chance) { - Map> map = LAND_GRASS_BIOMES.get(biome); - if (map == null) { - map = Maps.newHashMap(); - LAND_GRASS_BIOMES.put(biome, map); - } - WeightedList list = map.get(terrain); - if (list == null) { - list = new WeightedList(); - map.put(terrain, list); - } - TERRAIN.add(terrain); - list.add(plant, chance); - } - - public static void addWaterGrass(Block plant, Block... terrain) { - for (Block block : terrain) { - addWaterGrass(plant, block, 1F); - } - } - - public static void addWaterGrass(ResourceLocation biome, Block plant, Block... terrain) { - for (Block block : terrain) { - addWaterGrass(biome, plant, block, 1F); - } - } - - public static void addWaterGrass(Block plant, Block terrain, float chance) { - WeightedList list = WATER_GRASS_TYPES.get(terrain); - if (list == null) { - list = new WeightedList(); - WATER_GRASS_TYPES.put(terrain, list); - } - TERRAIN.add(terrain); - list.add(plant, chance); - } - - public static void addWaterGrass(ResourceLocation biome, Block plant, Block terrain, float chance) { - Map> map = WATER_GRASS_BIOMES.get(biome); - if (map == null) { - map = Maps.newHashMap(); - WATER_GRASS_BIOMES.put(biome, map); - } - WeightedList list = map.get(terrain); - if (list == null) { - list = new WeightedList(); - map.put(terrain, list); - } - TERRAIN.add(terrain); - list.add(plant, chance); - } - - public static Block getLandGrass(ResourceLocation biomeID, Block terrain, RandomSource random) { - Map> map = LAND_GRASS_BIOMES.get(biomeID); - WeightedList list = null; - if (map != null) { - list = map.get(terrain); - if (list == null) { - list = LAND_GRASS_TYPES.get(terrain); - } - } - else { - list = LAND_GRASS_TYPES.get(terrain); - } - return list == null ? null : list.get(random); - } - - public static Block getWaterGrass(ResourceLocation biomeID, Block terrain, RandomSource random) { - Map> map = WATER_GRASS_BIOMES.get(biomeID); - WeightedList list = null; - if (map != null) { - list = map.get(terrain); - if (list == null) { - list = WATER_GRASS_TYPES.get(terrain); - } - } - else { - list = WATER_GRASS_TYPES.get(terrain); - } - return list == null ? null : list.get(random); - } -} diff --git a/src/main/java/ru/bclib/api/ComposterAPI.java b/src/main/java/ru/bclib/api/ComposterAPI.java deleted file mode 100644 index ae9eda80..00000000 --- a/src/main/java/ru/bclib/api/ComposterAPI.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.api; - -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.Block; -import ru.bclib.mixin.common.ComposterBlockAccessor; - -public class ComposterAPI { - public static Block allowCompost(float chance, Block block){ - if (block != null) { - allowCompost(chance, block.asItem()); - } - return block; - } - - public static Item allowCompost(float chance, Item item){ - if (item != null && item != Items.AIR) { - ComposterBlockAccessor.callAdd(chance, item); - } - return item; - } -} diff --git a/src/main/java/ru/bclib/api/LifeCycleAPI.java b/src/main/java/ru/bclib/api/LifeCycleAPI.java deleted file mode 100644 index 95be9366..00000000 --- a/src/main/java/ru/bclib/api/LifeCycleAPI.java +++ /dev/null @@ -1,137 +0,0 @@ -package ru.bclib.api; - -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.world.level.CustomSpawner; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.ServerLevelData; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; - -/** - * provides some lifetime hooks for a Minecraft instance - */ -public class LifeCycleAPI { - private final static List onLoadLevelBiomes = new ArrayList<>(2); - private final static List onLoadLevel = new ArrayList<>(2); - private final static List beforeLoadLevel = new ArrayList<>(2); - /** - * A callback function that is used for each new ServerLevel instance - */ - public interface BeforeLevelLoadCall { - void beforeLoad(); - } - - /** - * A callback function that is used for each new ServerLevel instance - */ - public interface LevelLoadBiomesCall { - void onLoad(ServerLevel world, long seed, Registry registry); - } - - /** - * A callback function that is used for each new ServerLevel instance - */ - public interface LevelLoadCall { - void onLoad( - ServerLevel world, - MinecraftServer minecraftServer, - Executor executor, - LevelStorageSource.LevelStorageAccess levelStorageAccess, - ServerLevelData serverLevelData, - ResourceKey resourceKey, - ChunkProgressListener chunkProgressListener, - boolean bl, - long l, - List list, - boolean bl2); - } - - /** - * Register a callback that is called before a level is loaded or created, - * but after the {@link WorldDataAPI} was initialized and patches from - * the {@link ru.bclib.api.datafixer.DataFixerAPI} were applied. - * - * @param call The callback Method - */ - public static void beforeLevelLoad(BeforeLevelLoadCall call){ - beforeLoadLevel.add(call); - } - - /** - * Register a callback that is called when a new {@code ServerLevel is instantiated}. - * This callback will receive the world seed as well as it's biome registry. - * @param call The calbback Method - */ - public static void onLevelLoad(LevelLoadBiomesCall call){ - onLoadLevelBiomes.add(call); - } - - /** - * Register a callback that is called when a new {@code ServerLevel is instantiated}. - * This callbacl will receiv all parameters that were passed to the ServerLevel's constructor - * @param call The calbback Method - */ - public static void onLevelLoad(LevelLoadCall call){ - onLoadLevel.add(call); - } - - /** - * For internal use, You should not call this method! - */ - public static void _runBeforeLevelLoad(){ - beforeLoadLevel.forEach(c -> c.beforeLoad()); - } - /** - * For internal use, You should not call this method! - * @param minecraftServer - * @param executor - * @param levelStorageAccess - * @param serverLevelData - * @param resourceKey - * @param chunkProgressListener - * @param bl - * @param l - * @param list - * @param bl2 - */ - public static void _runLevelLoad(ServerLevel world, - MinecraftServer minecraftServer, - Executor executor, - LevelStorageSource.LevelStorageAccess levelStorageAccess, - ServerLevelData serverLevelData, - ResourceKey resourceKey, - ChunkProgressListener chunkProgressListener, - boolean bl, - long l, - List list, - boolean bl2){ - onLoadLevel.forEach(c -> c.onLoad( - world, - minecraftServer, - executor, - levelStorageAccess, - serverLevelData, - resourceKey, - chunkProgressListener, - bl, - l, - list, - bl2) - ); - - final long seed = world.getSeed(); - final Registry biomeRegistry = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY); - onLoadLevelBiomes.forEach(c -> c.onLoad(world, seed, biomeRegistry)); - } -} diff --git a/src/main/java/ru/bclib/api/ModIntegrationAPI.java b/src/main/java/ru/bclib/api/ModIntegrationAPI.java deleted file mode 100644 index b36eec68..00000000 --- a/src/main/java/ru/bclib/api/ModIntegrationAPI.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.bclib.api; - -import com.google.common.collect.Lists; -import net.fabricmc.loader.api.FabricLoader; -import ru.bclib.integration.ModIntegration; - -import java.util.List; - -public class ModIntegrationAPI { - private static final List INTEGRATIONS = Lists.newArrayList(); - private static final boolean HAS_CANVAS = FabricLoader.getInstance().isModLoaded("canvas"); - - /** - * Registers mod integration - * - * @param integration - * @return - */ - public static ModIntegration register(ModIntegration integration) { - INTEGRATIONS.add(integration); - return integration; - } - - /** - * Get all registered mod integrations. - * - * @return {@link List} of {@link ModIntegration}. - */ - public static List getIntegrations() { - return INTEGRATIONS; - } - - /** - * Initialize all integrations, only for internal usage. - */ - public static void registerAll() { - INTEGRATIONS.forEach(integration -> { - if (integration.modIsInstalled()) { - integration.init(); - } - }); - } - - public static boolean hasCanvas() { - return HAS_CANVAS; - } -} diff --git a/src/main/java/ru/bclib/api/PostInitAPI.java b/src/main/java/ru/bclib/api/PostInitAPI.java deleted file mode 100644 index fa3aa7f7..00000000 --- a/src/main/java/ru/bclib/api/PostInitAPI.java +++ /dev/null @@ -1,155 +0,0 @@ -package ru.bclib.api; - -import com.google.common.collect.Lists; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.core.Registry; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import ru.bclib.BCLib; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.api.tag.NamedMineableTags; -import ru.bclib.api.tag.TagAPI; - -import ru.bclib.blocks.BaseBarrelBlock; -import ru.bclib.blocks.BaseChestBlock; -import ru.bclib.blocks.BaseFurnaceBlock; -import ru.bclib.blocks.BaseSignBlock; -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.client.render.BaseChestBlockEntityRenderer; -import ru.bclib.client.render.BaseSignBlockEntityRenderer; -import ru.bclib.config.Configs; -import ru.bclib.interfaces.PostInitable; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.interfaces.TagProvider; -import ru.bclib.interfaces.tools.AddMineableAxe; -import ru.bclib.interfaces.tools.AddMineableHammer; -import ru.bclib.interfaces.tools.AddMineableHoe; -import ru.bclib.interfaces.tools.AddMineablePickaxe; -import ru.bclib.interfaces.tools.AddMineableShears; -import ru.bclib.interfaces.tools.AddMineableShovel; -import ru.bclib.interfaces.tools.AddMineableSword; -import ru.bclib.interfaces.tools.PreventMineableAdd; -import ru.bclib.registry.BaseBlockEntities; - -import java.util.List; -import java.util.function.Consumer; - -public class PostInitAPI { - private static List> postInitFunctions = Lists.newArrayList(); - private static List> blockTags = Lists.newArrayList(); - private static List> itemTags = Lists.newArrayList(); - - /** - * Register a new function which will be called after all mods are initiated. Will be called on both client and server. - * @param function {@link Consumer} with {@code boolean} parameter ({@code true} for client, {@code false} for server). - */ - public static void register(Consumer function) { - postInitFunctions.add(function); - } - - /** - * Called in proper BCLib entry points, for internal usage only. - * @param isClient {@code boolean}, {@code true} for client, {@code false} for server. - */ - public static void postInit(boolean isClient) { - if (postInitFunctions == null) { - return; - } - postInitFunctions.forEach(function -> function.accept(isClient)); - Registry.BLOCK.forEach(block -> { - processBlockCommon(block); - if (isClient) { - processBlockClient(block); - } - }); - - - Registry.ITEM.forEach(item -> { - processItemCommon(item); - }); - postInitFunctions = null; - blockTags = null; - itemTags = null; - BiomeAPI.loadFabricAPIBiomes(); - Configs.BIOMES_CONFIG.saveChanges(); - } - - @Environment(EnvType.CLIENT) - private static void processBlockClient(Block block) { - if (block instanceof RenderLayerProvider) { - BCLRenderLayer layer = ((RenderLayerProvider) block).getRenderLayer(); - if (layer == BCLRenderLayer.CUTOUT) BlockRenderLayerMap.INSTANCE.putBlock(block, RenderType.cutout()); - else if (layer == BCLRenderLayer.TRANSLUCENT) BlockRenderLayerMap.INSTANCE.putBlock(block, RenderType.translucent()); - } - if (block instanceof BaseChestBlock) { - BaseChestBlockEntityRenderer.registerRenderLayer(block); - } - else if (block instanceof BaseSignBlock) { - BaseSignBlockEntityRenderer.registerRenderLayer(block); - } - } - - private static void processItemCommon(Item item) { - if (item instanceof TagProvider provider){ - try { - provider.addTags(null, itemTags); - } catch (NullPointerException ex){ - BCLib.LOGGER.error(item + " probably tried to access blockTags.", ex); - } - itemTags.forEach(tag -> TagAPI.addItemTag(tag, item)); - itemTags.clear(); - } - } - - private static void processBlockCommon(Block block) { - if (block instanceof PostInitable) { - ((PostInitable) block).postInit(); - } - if (block instanceof BaseChestBlock) { - BaseBlockEntities.CHEST.registerBlock(block); - } - else if (block instanceof BaseSignBlock) { - BaseBlockEntities.SIGN.registerBlock(block); - } - else if (block instanceof BaseBarrelBlock) { - BaseBlockEntities.BARREL.registerBlock(block); - } - else if (block instanceof BaseFurnaceBlock) { - BaseBlockEntities.FURNACE.registerBlock(block); - } - if (!(block instanceof PreventMineableAdd)) { - if (block instanceof AddMineableShears) { - TagAPI.addBlockTags(block, NamedMineableTags.SHEARS); - } - if (block instanceof AddMineableAxe) { - TagAPI.addBlockTags(block, NamedMineableTags.AXE); - } - if (block instanceof AddMineablePickaxe) { - TagAPI.addBlockTags(block, NamedMineableTags.PICKAXE); - } - if (block instanceof AddMineableShovel) { - TagAPI.addBlockTags(block, NamedMineableTags.SHOVEL); - } - if (block instanceof AddMineableHoe) { - TagAPI.addBlockTags(block, NamedMineableTags.HOE); - } - if (block instanceof AddMineableSword) { - TagAPI.addBlockTags(block, NamedMineableTags.SWORD); - } - if (block instanceof AddMineableHammer) { - TagAPI.addBlockTags(block, NamedMineableTags.HAMMER); - } - } - if (block instanceof TagProvider) { - TagProvider.class.cast(block).addTags(blockTags, itemTags); - blockTags.forEach(tag -> TagAPI.addBlockTag(tag, block)); - itemTags.forEach(tag -> TagAPI.addItemTag(tag, block)); - blockTags.clear(); - itemTags.clear(); - } - } -} diff --git a/src/main/java/ru/bclib/api/ShovelAPI.java b/src/main/java/ru/bclib/api/ShovelAPI.java deleted file mode 100644 index 488d16cd..00000000 --- a/src/main/java/ru/bclib/api/ShovelAPI.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.bclib.api; - -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.mixin.common.ShovelItemAccessor; - -import java.util.Map; - -public class ShovelAPI { - /** - * Will add left-click behaviour to shovel: when it is targeting cetrain {@link Block} it will be converting to new - * {@link BlockState} on usage. Example: grass converting to path. - * @param target {@link Block} that will be converted. - * @param convert {@link BlockState} to convert block into. - */ - public static void addShovelBehaviour(Block target, BlockState convert) { - Map map = ShovelItemAccessor.bclib_getFlattenables(); - map.put(target, convert); - } -} diff --git a/src/main/java/ru/bclib/api/WorldDataAPI.java b/src/main/java/ru/bclib/api/WorldDataAPI.java deleted file mode 100644 index d15cf43b..00000000 --- a/src/main/java/ru/bclib/api/WorldDataAPI.java +++ /dev/null @@ -1,149 +0,0 @@ -package ru.bclib.api; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; -import ru.bclib.BCLib; -import ru.bclib.api.datafixer.DataFixerAPI; -import ru.bclib.util.ModUtil; - -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; - -/** - * Mod-specifix data-storage for a world. - *

- * This class provides the ability for mod to store persistent data inside a world. The Storage for the world is - * currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)} - * or {@link DataFixerAPI#initializeWorldData(File, boolean)} - */ -public class WorldDataAPI { - private static final Map TAGS = Maps.newHashMap(); - private static final List MODS = Lists.newArrayList(); - private static File dataDir; - - public static void load(File dataDir) { - WorldDataAPI.dataDir = dataDir; - MODS.stream() - .parallel() - .forEach(modID -> { - File file = new File(dataDir, modID + ".nbt"); - CompoundTag root = new CompoundTag(); - if (file.exists()) { - try { - root = NbtIo.readCompressed(file); - } - catch (IOException e) { - BCLib.LOGGER.error("World data loading failed", e); - } - TAGS.put(modID, root); - } - else { - Optional optional = FabricLoader.getInstance() - .getModContainer(modID); - if (optional.isPresent()) { - ModContainer modContainer = optional.get(); - if (BCLib.isDevEnvironment()) { - root.putString("version", "255.255.9999"); - } - else { - root.putString("version", modContainer.getMetadata() - .getVersion() - .toString()); - } - TAGS.put(modID, root); - saveFile(modID); - } - } - }); - } - - /** - * Register mod cache, world cache is located in world data folder. - * - * @param modID - {@link String} modID. - */ - public static void registerModCache(String modID) { - MODS.add(modID); - } - - /** - * Get root {@link CompoundTag} for mod cache in world data folder. - * - * @param modID - {@link String} modID. - * @return {@link CompoundTag} - */ - public static CompoundTag getRootTag(String modID) { - CompoundTag root = TAGS.get(modID); - if (root == null) { - root = new CompoundTag(); - TAGS.put(modID, root); - } - return root; - } - - /** - * Get {@link CompoundTag} with specified path from mod cache in world data folder. - * - * @param modID - {@link String} path to tag, dot-separated. - * @return {@link CompoundTag} - */ - public static CompoundTag getCompoundTag(String modID, String path) { - String[] parts = path.split("\\."); - CompoundTag tag = getRootTag(modID); - for (String part : parts) { - if (tag.contains(part)) { - tag = tag.getCompound(part); - } - else { - CompoundTag t = new CompoundTag(); - tag.put(part, t); - tag = t; - } - } - return tag; - } - - /** - * Forces mod cache file to be saved. - * - * @param modID {@link String} mod ID. - */ - public static void saveFile(String modID) { - try { - if (!dataDir.exists()) { - dataDir.mkdirs(); - } - NbtIo.writeCompressed(getRootTag(modID), new File(dataDir, modID + ".nbt")); - } - catch (IOException e) { - BCLib.LOGGER.error("World data saving failed", e); - } - } - - /** - * Get stored mod version (only for mods with registered cache). - * - * @return {@link String} mod version. - */ - public static String getModVersion(String modID) { - return getRootTag(modID).getString("version"); - } - - /** - * Get stored mod version as integer (only for mods with registered cache). - * - * @return {@code int} mod version. - */ - public static int getIntModVersion(String modID) { - return ModUtil.convertModVersion(getModVersion(modID)); - } -} diff --git a/src/main/java/ru/bclib/api/biomes/BCLBiomeBuilder.java b/src/main/java/ru/bclib/api/biomes/BCLBiomeBuilder.java deleted file mode 100644 index 9053b39f..00000000 --- a/src/main/java/ru/bclib/api/biomes/BCLBiomeBuilder.java +++ /dev/null @@ -1,742 +0,0 @@ -package ru.bclib.api.biomes; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.biome.v1.BiomeModifications; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.data.worldgen.BiomeDefaultFeatures; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.Music; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.tags.TagKey; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.level.biome.*; -import net.minecraft.world.level.biome.Biome.BiomeBuilder; -import net.minecraft.world.level.biome.Biome.Precipitation; -import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.GenerationStep.Decoration; -import net.minecraft.world.level.levelgen.Noises; -import net.minecraft.world.level.levelgen.SurfaceRules; -import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import ru.bclib.api.surface.SurfaceRuleBuilder; -import ru.bclib.entity.BCLEntityWrapper; -import ru.bclib.mixin.common.BiomeGenerationSettingsAccessor; -import ru.bclib.util.CollectionsUtil; -import ru.bclib.util.ColorUtil; -import ru.bclib.util.Pair; -import ru.bclib.util.TriFunction; -import ru.bclib.world.biomes.BCLBiome; -import ru.bclib.world.biomes.BCLBiomeSettings; -import ru.bclib.world.features.BCLFeature; -import ru.bclib.world.structures.BCLStructure; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Consumer; - -public class BCLBiomeBuilder { - @FunctionalInterface - public interface BiomeSupplier extends TriFunction{ - } - - private static final BCLBiomeBuilder INSTANCE = new BCLBiomeBuilder(); - private static final SurfaceRules.ConditionSource SURFACE_NOISE = SurfaceRules.noiseCondition(Noises.SOUL_SAND_LAYER, -0.012); - - private List> structureTags = new ArrayList<>(8); - private List>>> carvers = new ArrayList<>(1); - private BiomeGenerationSettings.Builder generationSettings; - private BiomeSpecialEffects.Builder effectsBuilder; - private MobSpawnSettings.Builder spawnSettings; - private SurfaceRules.RuleSource surfaceRule; - private Precipitation precipitation; - private ResourceLocation biomeID; - - private List parameters = Lists.newArrayList(); - - //BiomeTags.IS_NETHER - private float temperature; - private float fogDensity; - private float genChance; - private float downfall; - private float height; - private int edgeSize; - private BCLBiome edge; - private boolean vertical; - - - /** - * Starts new biome building process. - * @param biomeID {@link ResourceLocation} biome identifier. - * @return prepared {@link BCLBiomeBuilder} instance. - */ - public static BCLBiomeBuilder start(ResourceLocation biomeID) { - INSTANCE.biomeID = biomeID; - INSTANCE.precipitation = Precipitation.NONE; - INSTANCE.generationSettings = null; - INSTANCE.effectsBuilder = null; - INSTANCE.spawnSettings = null; - INSTANCE.structureTags.clear(); - INSTANCE.temperature = 1.0F; - INSTANCE.fogDensity = 1.0F; - INSTANCE.edgeSize = 0; - INSTANCE.downfall = 1.0F; - INSTANCE.genChance = 1.0F; - INSTANCE.height = 0.1F; - INSTANCE.vertical = false; - INSTANCE.edge = null; - INSTANCE.carvers.clear(); - INSTANCE.parameters.clear(); - return INSTANCE; - } - - public BCLBiomeBuilder addNetherClimateParamater(float temperature, float humidity){ - parameters.add(Climate.parameters(temperature, humidity, 0,0,0,0,0)); - return this; - } - - /** - * Set biome {@link Precipitation}. Affect biome visual effects (rain, snow, none). - * @param precipitation {@link Precipitation} - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder precipitation(Precipitation precipitation) { - this.precipitation = precipitation; - return this; - } - - /** - * Set biome temperature, affect plant color, biome generation and ice formation. - * @param temperature biome temperature. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder temperature(float temperature) { - this.temperature = temperature; - return this; - } - - /** - * Set biome wetness (same as downfall). Affect plant color and biome generation. - * @param wetness biome wetness (downfall). - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder wetness(float wetness) { - this.downfall = wetness; - return this; - } - - /** - * Adds mob spawning to biome. - * @param entityType {@link EntityType} mob type. - * @param weight spawn weight. - * @param minGroupCount minimum mobs in group. - * @param maxGroupCount maximum mobs in group. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder spawn(EntityType entityType, int weight, int minGroupCount, int maxGroupCount) { - getSpawns().addSpawn(entityType.getCategory(), new SpawnerData(entityType, weight, minGroupCount, maxGroupCount)); - return this; - } - - /** - * Adds mob spawning to biome. - * @param wrapper {@link BCLEntityWrapper} mob type. - * @param weight spawn weight. - * @param minGroupCount minimum mobs in group. - * @param maxGroupCount maximum mobs in group. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder spawn(BCLEntityWrapper wrapper, int weight, int minGroupCount, int maxGroupCount) { - if (wrapper.canSpawn()) { - return spawn(wrapper.type(), weight, minGroupCount, maxGroupCount); - } - - return this; - } - - /** - * Adds ambient particles to thr biome. - * @param particle {@link ParticleOptions} particles (or {@link net.minecraft.core.particles.ParticleType}). - * @param probability particle spawn probability, should have low value (example: 0.01F). - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder particles(ParticleOptions particle, float probability) { - getEffects().ambientParticle(new AmbientParticleSettings(particle, probability)); - return this; - } - - /** - * Sets sky color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder skyColor(int color) { - getEffects().skyColor(color); - return this; - } - - /** - * Sets sky color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder skyColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return skyColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets fog color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder fogColor(int color) { - getEffects().fogColor(color); - return this; - } - - /** - * Sets fog color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder fogColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return fogColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets fog density for the biome. - * @param density fog density as a float, default value is 1.0F. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder fogDensity(float density) { - this.fogDensity = density; - return this; - } - - /** - * Sets generation chance for this biome. - * @param genChance - * @return same {@link BCLBiomeBuilder}. - */ - public BCLBiomeBuilder genChance(float genChance) { - this.genChance = genChance; - return this; - } - - /** - * Sets edge size for this biome. - * @param edgeSize size of the Edge (in Blocks) - * @return same {@link BCLBiomeBuilder}. - */ - public BCLBiomeBuilder edgeSize(int edgeSize) { - this.edgeSize = edgeSize; - return this; - } - - /** - * Sets edge-Biome for this biome. - * @param edge The Edge Biome - * @return same {@link BCLBiomeBuilder}. - */ - public BCLBiomeBuilder edge(BCLBiome edge) { - this.edge = edge; - return this; - } - - - - /** - * Sets edge-Biome for this biome. - * @param edge The Edge Biome - * @param edgeSize size of the Edge (in Blocks) - * @return same {@link BCLBiomeBuilder}. - */ - public BCLBiomeBuilder edge(BCLBiome edge, int edgeSize) { - this.edge(edge); - this.edgeSize(edgeSize); - return this; - } - - /** - * Sets water color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder waterColor(int color) { - getEffects().waterColor(color); - return this; - } - - /** - * Sets water color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder waterColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return waterColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets underwater fog color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder waterFogColor(int color) { - getEffects().waterFogColor(color); - return this; - } - - /** - * Sets underwater fog color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder waterFogColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return waterFogColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets water and underwater fig color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder waterAndFogColor(int color) { - return waterColor(color).waterFogColor(color); - } - - /** - * Sets water and underwater fig color for the biome. Color is in ARGB int format. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder waterAndFogColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return waterAndFogColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets grass color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder grassColor(int color) { - getEffects().grassColorOverride(color); - return this; - } - - /** - * Sets grass color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder grassColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return grassColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets leaves and plants color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder foliageColor(int color) { - getEffects().foliageColorOverride(color); - return this; - } - - /** - * Sets leaves and plants color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder foliageColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return foliageColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets grass, leaves and all plants color for the biome. Color is in ARGB int format. - * @param color ARGB color as integer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder plantsColor(int color) { - return grassColor(color).foliageColor(color); - } - - /** - * Sets grass, leaves and all plants color for the biome. Color represented as red, green and blue channel values. - * @param red red color component [0-255] - * @param green green color component [0-255] - * @param blue blue color component [0-255] - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder plantsColor(int red, int green, int blue) { - red = Mth.clamp(red, 0, 255); - green = Mth.clamp(green, 0, 255); - blue = Mth.clamp(blue, 0, 255); - return plantsColor(ColorUtil.color(red, green, blue)); - } - - /** - * Sets biome music, used for biomes in the Nether and End. - * @param music {@link Music} to use. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder music(Music music) { - getEffects().backgroundMusic(music); - return this; - } - - /** - * Sets biome music, used for biomes in the Nether and End. - * @param music {@link SoundEvent} to use. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder music(SoundEvent music) { - return music(new Music(music, 600, 2400, true)); - } - - /** - * Sets biome ambient loop sound. Can be used for biome environment. - * @param loopSound {@link SoundEvent} to use as a loop. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder loop(SoundEvent loopSound) { - getEffects().ambientLoopSound(loopSound); - return this; - } - - /** - * Sets biome mood sound. Can be used for biome environment. - * @param mood {@link SoundEvent} to use as a mood. - * @param tickDelay delay between sound events in ticks. - * @param blockSearchExtent block search radius (for area available for sound). - * @param soundPositionOffset offset in sound. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder mood(SoundEvent mood, int tickDelay, int blockSearchExtent, float soundPositionOffset) { - getEffects().ambientMoodSound(new AmbientMoodSettings(mood, tickDelay, blockSearchExtent, soundPositionOffset)); - return this; - } - - /** - * Sets biome mood sound. Can be used for biome environment. - * @param mood {@link SoundEvent} to use as a mood. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder mood(SoundEvent mood) { - return mood(mood, 6000, 8, 2.0F); - } - - /** - * Sets biome additionsl ambient sounds. - * @param additions {@link SoundEvent} to use. - * @param intensity sound intensity. Default is 0.0111F. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder additions(SoundEvent additions, float intensity) { - getEffects().ambientAdditionsSound(new AmbientAdditionsSettings(additions, intensity)); - return this; - } - - /** - * Sets biome additionsl ambient sounds. - * @param additions {@link SoundEvent} to use. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder additions(SoundEvent additions) { - return additions(additions, 0.0111F); - } - - /** - * Adds new feature to the biome. - * @param decoration {@link Decoration} feature step. - * @param feature {@link PlacedFeature}. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder feature(Decoration decoration, Holder feature) { - getGeneration().addFeature(decoration, feature); - return this; - } - - /** - * Adds vanilla Mushrooms. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder defaultMushrooms() { - return feature(BiomeDefaultFeatures::addDefaultMushrooms); - } - - /** - * Adds vanilla Nether Ores. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder netherDefaultOres() { - return feature(BiomeDefaultFeatures::addNetherDefaultOres); - } - - /** - * Will add features into biome, used for vanilla feature adding functions. - * @param featureAdd {@link Consumer} with {@link BiomeGenerationSettings.Builder}. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder feature(Consumer featureAdd) { - featureAdd.accept(getGeneration()); - return this; - } - - /** - * Adds new feature to the biome. - * @param feature {@link BCLFeature}. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder feature(BCLFeature feature) { - return feature(feature.getDecoration(), feature.getPlacedFeature()); - } - - /** - * Adds new structure feature into the biome. - * @param structureTag {@link TagKey} to add. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder structure(TagKey structureTag) { - structureTags.add(structureTag); - return this; - } - - /** - * Adds new structure feature into thr biome. Will add building biome into the structure list. - * @param structure {@link BCLStructure} to add. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder structure(BCLStructure structure) { - structure.addInternalBiome(biomeID); - return structure(structure.biomeTag); - } - - /** - * Adds new world carver into the biome. - * @param carver {@link ConfiguredWorldCarver} to add. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder carver(GenerationStep.Carving step, Holder> carver) { - final ResourceLocation immutableID = biomeID; - var oKey = carver.unwrapKey(); - if (oKey.isPresent()) { - BiomeModifications.addCarver(ctx -> ctx.getBiomeKey().location().equals(immutableID), step, (ResourceKey>) oKey.get()); - } - //carvers.add(new Pair<>(step, carver)); - return this; - } - - /** - * Adds new world surface rule for the given block - * @param surfaceBlock {@link Block} to use. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder surface(Block surfaceBlock) { - return surface(surfaceBlock.defaultBlockState()); - } - - /** - * Adds new world surface rule for the given block - * @param surfaceBlock {@link BlockState} to use. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder surface(BlockState surfaceBlock) { - return surface(SurfaceRuleBuilder.start().surface(surfaceBlock).build()); - } - - /** - * Adds blocks to the biome surface and below it (with specified depth). - * @param surfaceBlock {@link Block} that will cover biome. - * @param subterrainBlock {@link Block} below it with specified depth. - * @param depth thickness of bottom block layer. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder surface(Block surfaceBlock, Block subterrainBlock, int depth) { - return surface(SurfaceRuleBuilder - .start() - .surface(surfaceBlock.defaultBlockState()) - .subsurface(subterrainBlock.defaultBlockState(), depth) - .build()); - } - - /** - * Adds surface rule to this biome. - * @param newSurfaceRule {link SurfaceRules.RuleSource} surface rule. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder surface(SurfaceRules.RuleSource newSurfaceRule) { - this.surfaceRule = newSurfaceRule; - return this; - } - - /** - * Set terrain height for the biome. Can be used in custom generators, doesn't change vanilla biome distribution or generation. - * @param height a relative float terrain height value. - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder terrainHeight(float height) { - this.height = height; - return this; - } - - - - /** - * Make this a vertical Biome - * - * @return same {@link BCLBiomeBuilder} instance. - */ - public BCLBiomeBuilder vertical() { - this.vertical = vertical; - return this; - } - - /** - * Finalize biome creation. - * @return created {@link BCLBiome} instance. - */ - public BCLBiome build() { - return build((BiomeSupplier)BCLBiome::new); - } - - /** - * Finalize biome creation. - * @param biomeConstructor {@link BiFunction} biome constructor. - * @return created {@link BCLBiome} instance. - */ - @Deprecated(forRemoval = true) - public T build(BiFunction biomeConstructor) { - return build((id, biome, settings)->biomeConstructor.apply(id, biome)); - } - - private static BiomeGenerationSettings fixGenerationSettings(BiomeGenerationSettings settings){ - //Fabric Biome Modification API can not handle an empty carver map, thus we will create one with - //an empty HolderSet for every possible step: - //https://github.com/FabricMC/fabric/issues/2079 - //TODO: Remove, once fabric gets fixed - if (settings instanceof BiomeGenerationSettingsAccessor acc){ - Map>> carvers = CollectionsUtil.getMutable(acc.bclib_getCarvers()); - for (GenerationStep.Carving step : GenerationStep.Carving.values()){ - carvers.computeIfAbsent(step, __->HolderSet.direct(Lists.newArrayList())); - } - acc.bclib_setCarvers(carvers); - } - return settings; - } - - /** - * Finalize biome creation. - * @param biomeConstructor {@link BiomeSupplier} biome constructor. - * @return created {@link BCLBiome} instance. - */ - public T build(BiomeSupplier biomeConstructor) { - BiomeBuilder builder = new BiomeBuilder() - .precipitation(precipitation) - .temperature(temperature) - .downfall(downfall); - - builder.mobSpawnSettings(getSpawns().build()); - builder.specialEffects(getEffects().build()); - - builder.generationSettings(fixGenerationSettings(getGeneration().build())); - - BCLBiomeSettings settings = BCLBiomeSettings.createBCL() - .setTerrainHeight(height) - .setFogDensity(fogDensity) - .setGenChance(genChance) - .setEdgeSize(edgeSize) - .setEdge(edge) - .setVertical(vertical) - .build(); - - final Biome biome = builder.build(); - final T res = biomeConstructor.apply(biomeID, biome, settings); - res.attachStructures(structureTags); - res.setSurface(surfaceRule); - res.addClimateParameters(parameters); - - //carvers.forEach(cfg -> BiomeAPI.addBiomeCarver(biome, cfg.second, cfg.first)); - return res; - } - - /** - * Get or create {@link BiomeSpecialEffects.Builder} for biome visual effects. - * For internal usage only. - * For internal usage only. - * @return new or same {@link BiomeSpecialEffects.Builder} instance. - */ - private BiomeSpecialEffects.Builder getEffects() { - if (effectsBuilder == null) { - effectsBuilder = new BiomeSpecialEffects.Builder(); - } - return effectsBuilder; - } - - /** - * Get or create {@link MobSpawnSettings.Builder} for biome mob spawning. - * For internal usage only. - * @return new or same {@link MobSpawnSettings.Builder} instance. - */ - private MobSpawnSettings.Builder getSpawns() { - if (spawnSettings == null) { - spawnSettings = new MobSpawnSettings.Builder(); - } - return spawnSettings; - } - - /** - * Get or create {@link BiomeGenerationSettings.Builder} for biome features and generation. - * For internal usage only. - * @return new or same {@link BiomeGenerationSettings.Builder} instance. - */ - private BiomeGenerationSettings.Builder getGeneration() { - if (generationSettings == null) { - generationSettings = new BiomeGenerationSettings.Builder(); - } - return generationSettings; - } -} diff --git a/src/main/java/ru/bclib/api/biomes/BiomeAPI.java b/src/main/java/ru/bclib/api/biomes/BiomeAPI.java deleted file mode 100644 index 172aa299..00000000 --- a/src/main/java/ru/bclib/api/biomes/BiomeAPI.java +++ /dev/null @@ -1,968 +0,0 @@ -package ru.bclib.api.biomes; - -import com.google.common.base.Suppliers; -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.impl.biome.NetherBiomeData; -import net.fabricmc.fabric.impl.biome.TheEndBiomeData; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.tags.BiomeTags; -import net.minecraft.util.random.WeightedRandomList; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.Biomes; -import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.PalettedContainer; -import net.minecraft.world.level.levelgen.GenerationStep.Carving; -import net.minecraft.world.level.levelgen.GenerationStep.Decoration; -import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; -import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -import net.minecraft.world.level.levelgen.SurfaceRules; -import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; -import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import org.apache.commons.lang3.mutable.MutableInt; -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; -import ru.bclib.api.tag.CommonBiomeTags; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.entity.BCLEntityWrapper; -import ru.bclib.interfaces.BiomeSourceAccessor; -import ru.bclib.interfaces.NoiseGeneratorSettingsProvider; -import ru.bclib.interfaces.SurfaceMaterialProvider; -import ru.bclib.interfaces.SurfaceProvider; -import ru.bclib.interfaces.SurfaceRuleProvider; -import ru.bclib.mixin.common.BiomeGenerationSettingsAccessor; -import ru.bclib.mixin.common.MobSpawnSettingsAccessor; -import ru.bclib.util.CollectionsUtil; -import ru.bclib.world.biomes.BCLBiome; -import ru.bclib.world.biomes.FabricBiomesData; -import ru.bclib.world.biomes.VanillaBiomeSettings; -import ru.bclib.world.features.BCLFeature; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; - -import net.minecraft.world.level.levelgen.structure.Structure; - -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class BiomeAPI { - public static class Dimension { - public static final Dimension NONE = new Dimension(); - public static final Dimension OVERWORLD = new Dimension(); - public static final Dimension NETHER = new Dimension(); - public static final Dimension END = new Dimension(); - public static final Dimension END_LAND = new Dimension(END); - public static final Dimension END_VOID = new Dimension(END); - - private static final Map DIMENSION_MAP = Maps.newHashMap(); - public final Dimension parentOrNull; - - public Dimension() { - this(null); - } - - public Dimension(Dimension parentOrNull) { - this.parentOrNull = parentOrNull; - } - - public boolean is(Dimension d){ - if (d==this) return true; - if (parentOrNull!=null) return parentOrNull.is(d); - return false; - } - } - /** - * Empty biome used as default value if requested biome doesn't exist or linked. Shouldn't be registered anywhere to prevent bugs. - * Have {@code Biomes.THE_VOID} as the reference biome. - */ - public static final BCLBiome EMPTY_BIOME = new BCLBiome(Biomes.THE_VOID.location()); - - private static final Map ID_MAP = Maps.newHashMap(); - private static final Map CLIENT = Maps.newHashMap(); - public static Registry biomeRegistry; - - private static final Map, Integer> FEATURE_ORDER = Maps.newHashMap(); - private static final MutableInt FEATURE_ORDER_ID = new MutableInt(0); - - private static final Map>>> MODIFICATIONS = Maps.newHashMap(); - private static final Map>>> TAG_ADDERS = Maps.newHashMap(); - private static final Map SURFACE_RULES = Maps.newHashMap(); - private static final Set MODIFIED_SURFACE_PROVIDERS = new HashSet<>(8); - - public static final BCLBiome NETHER_WASTES_BIOME = registerNetherBiome(getFromRegistry(Biomes.NETHER_WASTES).value()); - public static final BCLBiome CRIMSON_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.CRIMSON_FOREST).value()); - public static final BCLBiome WARPED_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.WARPED_FOREST).value()); - public static final BCLBiome SOUL_SAND_VALLEY_BIOME = registerNetherBiome(getFromRegistry(Biomes.SOUL_SAND_VALLEY).value()); - public static final BCLBiome BASALT_DELTAS_BIOME = registerNetherBiome(getFromRegistry(Biomes.BASALT_DELTAS).value()); - - public static final BCLBiome THE_END = registerEndLandBiome(getFromRegistry(Biomes.THE_END)); - public static final BCLBiome END_MIDLANDS = registerSubBiome(THE_END, getFromRegistry(Biomes.END_MIDLANDS).value(), 0.5F); - public static final BCLBiome END_HIGHLANDS = registerSubBiome(THE_END, getFromRegistry(Biomes.END_HIGHLANDS).value(), 0.5F); - - public static final BCLBiome END_BARRENS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("end_barrens"))); - public static final BCLBiome SMALL_END_ISLANDS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("small_end_islands"))); - - private static void initFeatureOrder() { - if (!FEATURE_ORDER.isEmpty()) { - return; - } - - BuiltinRegistries.BIOME - .entrySet() - .stream() - .filter(entry -> entry - .getKey() - .location() - .getNamespace() - .equals("minecraft")) - .map(Entry::getValue) - .map(biome -> (BiomeGenerationSettingsAccessor) biome.getGenerationSettings()) - .map(BiomeGenerationSettingsAccessor::bclib_getFeatures) - .forEach(stepFeatureSuppliers -> stepFeatureSuppliers.forEach(step -> step.forEach(feature -> { - FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement()); - }))); - } - - /** - * Initialize registry for current server. - * @param biomeRegistry - {@link Registry} for {@link Biome}. - */ - public static void initRegistry(Registry biomeRegistry) { - if (biomeRegistry != BiomeAPI.biomeRegistry) { - BiomeAPI.biomeRegistry = biomeRegistry; - CLIENT.clear(); - } - } - - /** - * For internal use only. - * - * This method gets called before a world is loaded/created to flush cashes we build. The Method is - * called from {@link ru.bclib.mixin.client.MinecraftMixin} - */ - public static void prepareNewLevel(){ - MODIFIED_SURFACE_PROVIDERS.forEach(p->p.bclib_clearBiomeSources()); - MODIFIED_SURFACE_PROVIDERS.clear(); - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * @param bclbiome {@link BCLBiome} - * @param dim The Dimension fo rthis Biome - * @return {@link BCLBiome} - */ - public static BCLBiome registerBiome(BCLBiome bclbiome, Dimension dim) { - if (BuiltinRegistries.BIOME.get(bclbiome.getID()) == null) { - final Biome biome = bclbiome.getBiome(); - ResourceLocation loc = bclbiome.getID(); - Registry.register(BuiltinRegistries.BIOME, loc, biome); - } - ID_MAP.put(bclbiome.getID(), bclbiome); - Dimension.DIMENSION_MAP.put(bclbiome.getID(), dim); - - if (dim!=null && dim.is(Dimension.NETHER)) { - TagAPI.addBiomeTag(BiomeTags.IS_NETHER, bclbiome.getBiome()); - TagAPI.addBiomeTag(CommonBiomeTags.IN_NETHER, bclbiome.getBiome()); - } else if (dim!=null && dim.is(Dimension.END)) { - TagAPI.addBiomeTag(BiomeTags.IS_END, bclbiome.getBiome()); - } - - bclbiome.afterRegistration(); - - return bclbiome; - } - - public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome) { - final Dimension dim = Dimension.DIMENSION_MAP.getOrDefault(parent.getID(), Dimension.NONE); - registerBiome(subBiome, dim); - parent.addSubBiome(subBiome); - - - return subBiome; - } - - public static BCLBiome registerSubBiome(BCLBiome parent, Biome biome, float genChance) { - BCLBiome subBiome = new BCLBiome(biome, VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); - return registerSubBiome(parent, subBiome); - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API. - * @param bclBiome {@link BCLBiome} - * @return {@link BCLBiome} - */ - public static BCLBiome registerNetherBiome(BCLBiome bclBiome) { - registerBiome(bclBiome, Dimension.NETHER); - - ResourceKey key = BiomeAPI.getBiomeKeyOrThrow(bclBiome.getBiomeHolder()); - bclBiome.forEachClimateParameter(p -> NetherBiomeData.addNetherBiome(key, p)); - return bclBiome; - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API. - * @param biome {@link BCLBiome} - * @return {@link BCLBiome} - */ - public static BCLBiome registerNetherBiome(Biome biome) { - BCLBiome bclBiome = new BCLBiome(biome, null); - registerBiome(bclBiome, Dimension.NETHER); - return bclBiome; - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). - * @param biome {@link BCLBiome} - * @return {@link BCLBiome} - */ - public static BCLBiome registerEndLandBiome(BCLBiome biome) { - registerBiome(biome, Dimension.END_LAND); - - float weight = biome.getGenChance(); - ResourceKey key = BiomeAPI.getBiomeKey(biome.getBiome()); - TheEndBiomeData.addEndBiomeReplacement(Biomes.END_HIGHLANDS, key, weight); - TheEndBiomeData.addEndBiomeReplacement(Biomes.END_MIDLANDS, key, weight); - return biome; - } - - /** - * Register {@link BCLBiome} wrapper for {@link Biome}. - * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). - * @param biome {@link BCLBiome} - * @return {@link BCLBiome} - */ - public static BCLBiome registerEndLandBiome(Holder biome) { - BCLBiome bclBiome = new BCLBiome(biome.value(), null); - - registerBiome(bclBiome, Dimension.END_LAND); - return bclBiome; - } - - /** - * Register {@link BCLBiome} wrapper for {@link Biome}. - * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). - * @param biome {@link BCLBiome}; - * @param genChance float generation chance. - * @return {@link BCLBiome} - */ - public static BCLBiome registerEndLandBiome(Holder biome, float genChance) { - BCLBiome bclBiome = new BCLBiome(biome.value(), VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); - - registerBiome(bclBiome, Dimension.END_LAND); - return bclBiome; - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). - * @param biome {@link BCLBiome} - * @return {@link BCLBiome} - */ - public static BCLBiome registerEndVoidBiome(BCLBiome biome) { - registerBiome(biome, Dimension.END_VOID); - - float weight = biome.getGenChance(); - ResourceKey key = BiomeAPI.getBiomeKeyOrThrow(biome.getBiomeHolder()); - TheEndBiomeData.addEndBiomeReplacement(Biomes.SMALL_END_ISLANDS, key, weight); - return biome; - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). - * @param biome {@link BCLBiome} - * @return {@link BCLBiome} - */ - public static BCLBiome registerEndVoidBiome(Holder biome) { - BCLBiome bclBiome = new BCLBiome(biome.value(), null); - - registerBiome(bclBiome, Dimension.END_VOID); - return bclBiome; - } - - /** - * Register {@link BCLBiome} instance and its {@link Biome} if necessary. - * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). - * @param biome {@link BCLBiome}. - * @param genChance float generation chance. - * @return {@link BCLBiome} - */ - public static BCLBiome registerEndVoidBiome(Holder biome, float genChance) { - BCLBiome bclBiome = new BCLBiome(biome.value(), VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); - - registerBiome(bclBiome, Dimension.END_VOID); - return bclBiome; - } - - /** - * Get {@link BCLBiome} from {@link Biome} instance on server. Used to convert world biomes to BCLBiomes. - * @param biome - {@link Holder} from world. - * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. - */ - public static BCLBiome getFromBiome(Holder biome) { - if (biomeRegistry == null) { - return EMPTY_BIOME; - } - return ID_MAP.getOrDefault(biome.unwrapKey().orElseThrow().location(), EMPTY_BIOME); - } - - /** - * Get {@link BCLBiome} from biome on client. Used in fog rendering. - * @param biome - {@link Biome} from client world. - * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. - */ - @Environment(EnvType.CLIENT) - public static BCLBiome getRenderBiome(Biome biome) { - BCLBiome endBiome = CLIENT.get(biome); - if (endBiome == null) { - Minecraft minecraft = Minecraft.getInstance(); - ResourceLocation id = minecraft.level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(biome); - endBiome = id == null ? EMPTY_BIOME : ID_MAP.getOrDefault(id, EMPTY_BIOME); - CLIENT.put(biome, endBiome); - } - return endBiome; - } - - /** - * Get biome {@link ResourceKey} from given {@link Biome}. - * @param biome - {@link Biome} from server world. - * @return biome {@link ResourceKey} or {@code null}. - */ - @Nullable - public static ResourceKey getBiomeKey(Biome biome) { - return BuiltinRegistries.BIOME - .getResourceKey(biome) - .orElseGet(() -> biomeRegistry != null ? biomeRegistry.getResourceKey(biome).orElse(null) : null); - } - - /** - * Get biome {@link ResourceLocation} from given {@link Biome}. - * @param biome - {@link Biome} from server world. - * @return biome {@link ResourceLocation}. - */ - public static ResourceLocation getBiomeID(Biome biome) { - ResourceLocation id = BuiltinRegistries.BIOME.getKey(biome); - if (id == null && biomeRegistry != null) { - id = biomeRegistry.getKey(biome); - } - return id == null ? EMPTY_BIOME.getID() : id; - } - - /** - * Get biome {@link ResourceLocation} from given {@link Biome}. - * @param biome - {@link Holder} from server world. - * @return biome {@link ResourceLocation}. - */ - public static ResourceLocation getBiomeID(Holder biome) { - var oKey = biome.unwrapKey(); - if (oKey.isPresent()){ - return oKey.get().location(); - } - return null; - } - - public static ResourceKey getBiomeKey(Holder biome) { - return biome.unwrapKey().orElse(null); - } - - public static ResourceKey getBiomeKeyOrThrow(Holder biome) { - return biome.unwrapKey().orElseThrow(); - } - - /** - * Get {@link BCLBiome} from given {@link ResourceLocation}. - * @param biomeID - biome {@link ResourceLocation}. - * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. - */ - public static BCLBiome getBiome(ResourceLocation biomeID) { - return ID_MAP.getOrDefault(biomeID, EMPTY_BIOME); - } - - /** - * Get {@link BCLBiome} from given {@link Biome}. - * @param biome - biome {@link Biome}. - * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. - */ - public static BCLBiome getBiome(Biome biome) { - return getBiome(BiomeAPI.getBiomeID(biome)); - } - - /** - * Get {@link BCLBiome} from given {@link Biome}. - * @param biome - biome {@link Biome}. - * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. - */ - public static BCLBiome getBiome(Holder biome) { - return getBiome(BiomeAPI.getBiomeID(biome)); - } - - /** - * Check if biome with {@link ResourceLocation} exists in API registry. - * @param biomeID - biome {@link ResourceLocation}. - * @return {@code true} if biome exists in API registry and {@code false} if not. - */ - public static boolean hasBiome(ResourceLocation biomeID) { - return ID_MAP.containsKey(biomeID); - } - - /** - * Load biomes from Fabric API. For internal usage only. - */ - public static void loadFabricAPIBiomes() { - FabricBiomesData.NETHER_BIOMES.forEach((key) -> { - if (!hasBiome(key.location())) { - registerNetherBiome(BuiltinRegistries.BIOME.get(key)); - } - }); - - FabricBiomesData.END_LAND_BIOMES.forEach((key, weight) -> { - if (!hasBiome(key.location())) { - registerEndLandBiome(BuiltinRegistries.BIOME.getHolder(key).orElseThrow(), weight); - } - }); - - FabricBiomesData.END_VOID_BIOMES.forEach((key, weight) -> { - if (!hasBiome(key.location())) { - registerEndVoidBiome(BuiltinRegistries.BIOME.getOrCreateHolder(key), weight); - } - }); - } - - @Nullable - public static Holder getFromRegistry(ResourceLocation key) { - return BuiltinRegistries.BIOME.getHolder(ResourceKey.create(Registry.BIOME_REGISTRY, key)).orElseThrow(); - } - - @Nullable - public static Holder getFromRegistry(ResourceKey key) { - return BuiltinRegistries.BIOME.getOrCreateHolder(key); - } - - public static boolean isDatapackBiome(ResourceLocation biomeID) { - return getFromRegistry(biomeID) == null; - } - - public static boolean wasRegisteredAs(ResourceLocation biomeID, Dimension dim) { - if (!Dimension.DIMENSION_MAP.containsKey(biomeID)) return false; - return Dimension.DIMENSION_MAP.get(biomeID).is(dim); - } - public static boolean wasRegisteredAsNetherBiome(ResourceLocation biomeID) { - return wasRegisteredAs(biomeID, Dimension.NETHER); - } - - public static boolean wasRegisteredAsEndBiome(ResourceLocation biomeID) { - return wasRegisteredAs(biomeID, Dimension.END); - } - - public static boolean wasRegisteredAsEndLandBiome(ResourceLocation biomeID) { - return wasRegisteredAs(biomeID, Dimension.END_LAND); - } - - public static boolean wasRegisteredAsEndVoidBiome(ResourceLocation biomeID) { - return wasRegisteredAs(biomeID, Dimension.END_VOID); - } - - /** - * 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> modification) { - List>> modifications = MODIFICATIONS.computeIfAbsent(dimensionID, k -> Lists.newArrayList()); - 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> 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> 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> modification) { - registerBiomeModification(Level.END, modification); - } - - /** - * For internal use only - */ - public static void _runTagAdders(){ - for (var mod:TAG_ADDERS.entrySet()) { - Stream s = null; - if (mod.getKey()==Level.NETHER) s = Dimension.DIMENSION_MAP.entrySet().stream().filter(e ->e.getValue().is(Dimension.NETHER)).map(e -> e.getKey()); - else if (mod.getKey()==Level.END) s = Dimension.DIMENSION_MAP.entrySet().stream().filter(e ->e.getValue().is(Dimension.END)).map(e -> e.getKey()); - if (s!=null) { - s.forEach(id -> { - BCLBiome b = BiomeAPI.getBiome(id); - Holder biomeHolder = b.getBiomeHolder(); - if (biomeHolder.isBound()) { - mod.getValue().forEach(c -> c.accept(id, biomeHolder)); - } - }); - } - } - } - - /** - * 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 onFinishingBiomeTags(ResourceKey dimensionID, BiConsumer> modification) { - List>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID, k -> Lists.newArrayList()); - modifications.add(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 onFinishingNetherBiomeTags(BiConsumer> modification) { - onFinishingBiomeTags(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 onFinishingEndBiomeTags(BiConsumer> modification) { - onFinishingBiomeTags(Level.END, modification); - } - - /** - * Will apply biome modiffications to world, internal usage only. - * @param level - */ - public static void applyModifications(ServerLevel level) { - NoiseGeneratorSettings noiseGeneratorSettings = null; - final ChunkGenerator chunkGenerator = level.getChunkSource().getGenerator(); - final BiomeSource source = chunkGenerator.getBiomeSource(); - final Set> biomes = source.possibleBiomes(); - - if (chunkGenerator instanceof NoiseGeneratorSettingsProvider gen) - noiseGeneratorSettings = gen.bclib_getNoiseGeneratorSettings(); - - // Datapacks (like Amplified Nether)will change the GeneratorSettings upon load, so we will - // only use the default Setting for Nether/End if we were unable to find a settings object - if (noiseGeneratorSettings==null){ - if (level.dimension() == Level.NETHER) { - noiseGeneratorSettings = BuiltinRegistries.NOISE_GENERATOR_SETTINGS.get(NoiseGeneratorSettings.NETHER); - } else if (level.dimension() == Level.END) { - noiseGeneratorSettings = BuiltinRegistries.NOISE_GENERATOR_SETTINGS.get(NoiseGeneratorSettings.END); - } - } - - List>> modifications = MODIFICATIONS.get(level.dimension()); - for (Holder biomeHolder : biomes) { - if (biomeHolder.isBound()) { - applyModificationsAndUpdateFeatures(modifications, biomeHolder); - } - } - - - if (noiseGeneratorSettings != null) { - final SurfaceRuleProvider provider = SurfaceRuleProvider.class.cast(noiseGeneratorSettings); - // Multiple Biomes can use the same generator. So we need to keep track of all Biomes that are - // Provided by all the BiomeSources that use the same generator. - // This happens for example when using the MiningDimensions, which reuses the generator for the - // Nethering Dimension - MODIFIED_SURFACE_PROVIDERS.add(provider); - provider.bclib_addBiomeSource(source); - } else { - BCLib.LOGGER.warning("No generator for " + source); - } - - ((BiomeSourceAccessor) source).bclRebuildFeatures(); - } - - private static void applyModificationsAndUpdateFeatures(List>> modifications, Holder biome) { - ResourceLocation biomeID = getBiomeID(biome); - if (modifications!=null) { - modifications.forEach(consumer -> { - consumer.accept(biomeID, biome); - }); - } - - sortBiomeFeatures(biome); - } - - /** - * Create a unique sort order for all Features of the Biome - * @param biome The {@link Biome} to sort the features for - */ - public static void sortBiomeFeatures(Holder biome) { - BiomeGenerationSettings settings = biome.value().getGenerationSettings(); - BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings; - List> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); - final int size = featureList.size(); - for (int i = 0; i < size; i++) { - List> features = getFeaturesListCopy(featureList, i); - sortFeatures(features); - featureList.set(i, HolderSet.direct(features)); - } - accessor.bclib_setFeatures(featureList); - } - - private static List getRuleSourcesForBiomes(Set> biomes) { - Set biomeIDs = biomes - .stream() - .map(biome -> getBiomeID(biome)) - .collect(Collectors.toSet()); - return getRuleSourcesFromIDs(biomeIDs); - } - - /** - * Creates a list of SurfaceRules for all Biomes that are managed by the passed {@link BiomeSource}. - * If we have Surface rules for any of the Biomes from the given set of {@link BiomeSource}, they - * will be added to the result - * - * Note: This Method is used in the {@link ru.bclib.mixin.common.NoiseGeneratorSettingsMixin} which in turn - * is called from {@link #applyModifications(ServerLevel)}. - * @param sources The Set of {@link BiomeSource} we want to consider - * @return A list of {@link RuleSource}-Objects that are needed to create those Biomes - */ - public static List getRuleSources(Set sources) { - final Set> biomes = new HashSet<>(); - for (BiomeSource s : sources) { - biomes.addAll(s.possibleBiomes()); - } - - return getRuleSourcesForBiomes(biomes); - } - - private static List getRuleSourcesFromIDs(Set biomeIDs) { - List rules = Lists.newArrayList(); - SURFACE_RULES.forEach((biomeID, rule) -> { - if (biomeIDs.contains(biomeID)) { - rules.add(rule); - } - }); - return rules; - } - - /** - * Adds new features to existing biome. - * @param biome {@link Biome} to add features in. - * @param feature {@link ConfiguredFeature} to add. - * - */ - public static void addBiomeFeature(Holder biome, BCLFeature feature) { - addBiomeFeature(biome, feature.getDecoration(), feature.getPlacedFeature()); - } - - /** - * Adds new features to existing biome. - * @param biome {@link Biome} to add features in. - * @param step a {@link Decoration} step for the feature. - * @param featureList {@link ConfiguredFeature} to add. - */ - public static void addBiomeFeature(Holder biome, Decoration step, Holder... featureList) { - addBiomeFeature(biome, step, List.of(featureList)); - } - - /** - * Adds new features to existing biome. - * @param biome {@link Biome} to add features in. - * @param step a {@link Decoration} step for the feature. - * @param additionalFeatures List of {@link ConfiguredFeature} to add. - */ - private static void addBiomeFeature(Holder biome, Decoration step, List> additionalFeatures) { - BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) biome.value().getGenerationSettings(); - List> allFeatures = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); - List> features = getFeaturesListCopy(allFeatures, step); - - for (var feature : additionalFeatures) { - if (!features.contains(feature)) - features.add(feature); - } - - allFeatures.set(step.ordinal(), HolderSet.direct(features)); - final Supplier>> flowerFeatures = Suppliers.memoize(() -> allFeatures.stream().flatMap(HolderSet::stream).map(Holder::value).flatMap(PlacedFeature::getFeatures).filter(configuredFeature -> configuredFeature.feature() == Feature.FLOWER).collect(ImmutableList.toImmutableList())); - final Supplier> featureSet = Suppliers.memoize(() -> allFeatures.stream().flatMap(HolderSet::stream).map(Holder::value).collect(Collectors.toSet())); - - accessor.bclib_setFeatures(allFeatures); - accessor.bclib_setFeatureSet(featureSet); - accessor.bclib_setFlowerFeatures(flowerFeatures); - } - - /** - * Adds new carver into existing biome. - * @param biome {@link Biome} to add carver in. - * @param carver {@link ConfiguredWorldCarver} to add. - * @param stage {@link Carving} stage. - */ - public static void addBiomeCarver(Biome biome, Holder> carver, Carving stage) { - BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) biome.getGenerationSettings(); - Map>> carverMap = CollectionsUtil.getMutable(accessor.bclib_getCarvers()); - HolderSet> carvers = carverMap.get(stage); - - List>> carverList; - if (carvers==null) { - carverList = Lists.newArrayList(); - } else { - carverList = carvers.stream().toList(); - } - carverList.add((Holder>)carver); - carverMap.put(stage, HolderSet.direct(carverList)); - accessor.bclib_setCarvers(carverMap); - } - - /** - * Adds surface rule to specified biome. - * @param biomeID biome {@link ResourceLocation}. - * @param source {@link SurfaceRules.RuleSource}. - */ - public static void addSurfaceRule(ResourceLocation biomeID, SurfaceRules.RuleSource source) { - SURFACE_RULES.put(biomeID, source); - //NOISE_GENERATOR_SETTINGS.forEach(BiomeAPI::changeSurfaceRulesForGenerator); - } - - /** - * Get surface rule for the biome using its {@link ResourceLocation} ID as a key. - * @param biomeID {@link ResourceLocation} biome ID. - * @return {@link SurfaceRules.RuleSource}. - */ - @Nullable - public static SurfaceRules.RuleSource getSurfaceRule(ResourceLocation biomeID) { - return SURFACE_RULES.get(biomeID); - } - - /** - * Adds mob spawning to specified biome. - * @param biome {@link Biome} to add mob spawning. - * @param entityType {@link BCLEntityWrapper} mob type. - * @param weight spawn weight. - * @param minGroupCount minimum mobs in group. - * @param maxGroupCount maximum mobs in group. - */ - public static void addBiomeMobSpawn(Holder biome, BCLEntityWrapper entityType, int weight, int minGroupCount, int maxGroupCount) { - if (entityType.canSpawn()){ - addBiomeMobSpawn(biome, entityType.type(), weight, minGroupCount, maxGroupCount); - } - } - - /** - * Adds mob spawning to specified biome. - * @param biome {@link Biome} to add mob spawning. - * @param entityType {@link EntityType} mob type. - * @param weight spawn weight. - * @param minGroupCount minimum mobs in group. - * @param maxGroupCount maximum mobs in group. - */ - public static void addBiomeMobSpawn(Holder biome, EntityType entityType, int weight, int minGroupCount, int maxGroupCount) { - final MobCategory category = entityType.getCategory(); - MobSpawnSettingsAccessor accessor = (MobSpawnSettingsAccessor) biome.value().getMobSettings(); - Map> spawners = CollectionsUtil.getMutable(accessor.bcl_getSpawners()); - List mobs = spawners.containsKey(category) ? CollectionsUtil.getMutable(spawners.get(category).unwrap()) : Lists.newArrayList(); - mobs.add(new SpawnerData(entityType, weight, minGroupCount, maxGroupCount)); - spawners.put(category, WeightedRandomList.create(mobs)); - accessor.bcl_setSpawners(spawners); - } - - /** - * Get biome surface block. Can be used to get terrain material for features or other things. - * @param pos {@link BlockPos} position to get block. - * @param biome {@link Holder} to get block from. - * @param level {@link ServerLevel} current server level. - * @return {@link BlockState} with the biome surface or AIR if it fails. - */ - public static BlockState getBiomeSurfaceBlock(BlockPos pos, Holder biome, ServerLevel level) { - ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (generator instanceof NoiseBasedChunkGenerator) { - SurfaceProvider provider = SurfaceProvider.class.cast(generator); - return provider.bclib_getSurface(pos, biome, level); - } - return Blocks.AIR.defaultBlockState(); - } - - public static Optional findTopMaterial(WorldGenLevel world, BlockPos pos){ - return findTopMaterial(getBiome(world.getBiome(pos))); - } - - public static Optional findTopMaterial(Holder biome){ - return findTopMaterial(getBiome(biome.value())); - } - - public static Optional findTopMaterial(Biome biome){ - return findTopMaterial(getBiome(biome)); - } - - public static Optional findTopMaterial(BCLBiome biome){ - if (biome instanceof SurfaceMaterialProvider smp){ - return Optional.of(smp.getTopMaterial()); - } - return Optional.empty(); - } - - public static Optional findUnderMaterial(WorldGenLevel world, BlockPos pos){ - return findUnderMaterial(getBiome(world.getBiome(pos))); - } - - public static Optional findUnderMaterial(Holder biome){ - return findUnderMaterial(getBiome(biome.value())); - } - - public static Optional findUnderMaterial(Biome biome){ - return findUnderMaterial(getBiome(biome)); - } - - public static Optional findUnderMaterial(BCLBiome biome){ - if (biome instanceof SurfaceMaterialProvider smp){ - return Optional.of(smp.getUnderMaterial()); - } - return Optional.empty(); - } - - /** - * Set biome in chunk at specified position. - * @param chunk {@link ChunkAccess} chunk to set biome in. - * @param pos {@link BlockPos} biome position. - * @param biome {@link Holder} instance. Should be biome from world. - */ - public static void setBiome(ChunkAccess chunk, BlockPos pos, Holder biome) { - int sectionY = (pos.getY() - chunk.getMinBuildHeight()) >> 4; - PalettedContainer> biomes = chunk.getSection(sectionY).getBiomes(); - biomes.set((pos.getX() & 15) >> 2, (pos.getY() & 15) >> 2, (pos.getZ() & 15) >> 2, biome); - } - - /** - * Set biome in world at specified position. - * @param level {@link LevelAccessor} world to set biome in. - * @param pos {@link BlockPos} biome position. - * @param biome {@link Holder} instance. Should be biome from world. - */ - public static void setBiome(LevelAccessor level, BlockPos pos, Holder biome) { - ChunkAccess chunk = level.getChunk(pos); - setBiome(chunk, pos, biome); - } - - static class StructureID { - public final ResourceLocation biomeID; - public final Holder structure; - - StructureID(ResourceLocation biomeID, Holder structure){ - this.biomeID = biomeID; - this.structure = structure; - } - - @Override - public String toString() { - return "StructureID{" + "id=" + biomeID + ", structure=" + structure + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StructureID that = (StructureID) o; - return biomeID.equals(that.biomeID) && structure.equals(that.structure); - } - - @Override - public int hashCode() { - return Objects.hash(biomeID, structure); - } - } - - private static void sortFeatures(List> features) { - initFeatureOrder(); - - Set> featuresWithoutDuplicates = Sets.newHashSet(); - features.forEach(holder -> featuresWithoutDuplicates.add(holder)); - - if (featuresWithoutDuplicates.size() != features.size()) { - features.clear(); - featuresWithoutDuplicates.forEach(feature -> features.add(feature)); - } - - features.forEach(feature -> { - FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement()); - }); - - features.sort((f1, f2) -> { - int v1 = FEATURE_ORDER.getOrDefault(f1, 70000); - int v2 = FEATURE_ORDER.getOrDefault(f2, 70000); - return Integer.compare(v1, v2); - }); - } - - /** - * Getter for correct feature list from all biome feature list of lists. - * @param step feature {@link Decoration} step. - * @param lists biome accessor lists. - * @return mutable {@link ConfiguredFeature} list. - */ - private static List> getList(Decoration step, List>> lists) { - int index = step.ordinal(); - if (lists.size() <= index) { - for (int i = lists.size(); i <= index; i++) { - lists.add(Lists.newArrayList()); - } - } - List> list = CollectionsUtil.getMutable(lists.get(index)); - lists.set(index, list); - return list; - } - - private static List> getFeaturesListCopy(List> features, Decoration step) { - return getFeaturesListCopy(features, step.ordinal()); - } - - private static List> getFeaturesListCopy(List> features, int index) { - while (features.size() <= index) { - features.add(HolderSet.direct(Lists.newArrayList())); - } - return features.get(index).stream().collect(Collectors.toList()); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/BaseDataHandler.java b/src/main/java/ru/bclib/api/dataexchange/BaseDataHandler.java deleted file mode 100644 index 47ca87af..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/BaseDataHandler.java +++ /dev/null @@ -1,99 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import org.jetbrains.annotations.NotNull; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -public abstract class BaseDataHandler { - private final boolean originatesOnServer; - @NotNull - private final ResourceLocation identifier; - - protected BaseDataHandler(ResourceLocation identifier, boolean originatesOnServer) { - this.originatesOnServer = originatesOnServer; - this.identifier = identifier; - } - - final public boolean getOriginatesOnServer() { - return originatesOnServer; - } - - final public ResourceLocation getIdentifier() { - return identifier; - } - - @Environment(EnvType.CLIENT) - abstract void receiveFromServer(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender); - - private ServerPlayer lastMessageSender; - - void receiveFromClient(MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender) { - lastMessageSender = player; - } - - final protected boolean reply(BaseDataHandler message, MinecraftServer server) { - if (lastMessageSender == null) return false; - message.sendToClient(server, lastMessageSender); - return true; - } - - abstract void sendToClient(MinecraftServer server); - - abstract void sendToClient(MinecraftServer server, ServerPlayer player); - - @Environment(EnvType.CLIENT) - abstract void sendToServer(Minecraft client); - - protected boolean isBlocking() { return false; } - - @Override - public String toString() { - return "BasDataHandler{" + "originatesOnServer=" + originatesOnServer + ", identifier=" + identifier + '}'; - } - - /** - * Write a String to a buffer (Convenience Method) - * - * @param buf The buffer to write to - * @param s The String you want to write - */ - public static void writeString(FriendlyByteBuf buf, String s) { - buf.writeByteArray(s.getBytes(StandardCharsets.UTF_8)); - } - - /** - * Read a string from a buffer (Convenience Method) - * - * @param buf Thea buffer to read from - * @return The received String - */ - public static String readString(FriendlyByteBuf buf) { - byte[] data = buf.readByteArray(); - return new String(data, StandardCharsets.UTF_8); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof BaseDataHandler)) return false; - BaseDataHandler that = (BaseDataHandler) o; - return originatesOnServer == that.originatesOnServer && identifier.equals(that.identifier); - } - - @Override - public int hashCode() { - return Objects.hash(originatesOnServer, identifier); - } -} - diff --git a/src/main/java/ru/bclib/api/dataexchange/Connector.java b/src/main/java/ru/bclib/api/dataexchange/Connector.java deleted file mode 100644 index 6f933489..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/Connector.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.bclib.api.dataexchange; - -import ru.bclib.api.dataexchange.handler.DataExchange; - -import java.util.Set; - -abstract class Connector { - protected final DataExchange api; - - Connector(DataExchange api) { - this.api = api; - } - public abstract boolean onClient(); - - protected Set getDescriptors(){ - return api.getDescriptors(); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/ConnectorClientside.java b/src/main/java/ru/bclib/api/dataexchange/ConnectorClientside.java deleted file mode 100644 index 6e192b91..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/ConnectorClientside.java +++ /dev/null @@ -1,70 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.network.FriendlyByteBuf; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.handler.DataExchange; - -/** - * This is an internal class that handles a Clienetside players Connection to a Server - */ -@Environment(EnvType.CLIENT) -public class ConnectorClientside extends Connector { - private Minecraft client; - ConnectorClientside(DataExchange api) { - super(api); - this.client = null; - } - - - @Override - public boolean onClient() { - return true; - } - - public void onPlayInit(ClientPacketListener handler, Minecraft client){ - if (this.client!=null && this.client != client){ - BCLib.LOGGER.warning("Client changed!"); - } - this.client = client; - for(DataHandlerDescriptor desc : getDescriptors()){ - ClientPlayNetworking.registerReceiver(desc.IDENTIFIER, (_client, _handler, _buf, _responseSender)->{ - receiveFromServer(desc, _client, _handler, _buf, _responseSender); - }); - } - } - - public void onPlayReady(ClientPacketListener handler, PacketSender sender, Minecraft client){ - for(DataHandlerDescriptor desc : getDescriptors()){ - if (desc.sendOnJoin){ - BaseDataHandler h = desc.JOIN_INSTANCE.get(); - if (!h.getOriginatesOnServer()) { - h.sendToServer(client); - } - } - } - } - - public void onPlayDisconnect(ClientPacketListener handler, Minecraft client){ - for(DataHandlerDescriptor desc : getDescriptors()) { - ClientPlayNetworking.unregisterReceiver(desc.IDENTIFIER); - } - } - - void receiveFromServer(DataHandlerDescriptor desc, Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender){ - BaseDataHandler h = desc.INSTANCE.get(); - h.receiveFromServer(client, handler, buf, responseSender); - } - - public void sendToServer(BaseDataHandler h){ - if (client==null){ - throw new RuntimeException("[internal error] Client not initialized yet!"); - } - h.sendToServer(this.client); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/ConnectorServerside.java b/src/main/java/ru/bclib/api/dataexchange/ConnectorServerside.java deleted file mode 100644 index f8debc99..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/ConnectorServerside.java +++ /dev/null @@ -1,67 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.handler.DataExchange; - -/** - * This is an internal class that handles a Serverside Connection to a Client-Player - */ -public class ConnectorServerside extends Connector { - private MinecraftServer server; - ConnectorServerside(DataExchange api) { - super(api); - server = null; - } - - @Override - public boolean onClient() { - return false; - } - - public void onPlayInit(ServerGamePacketListenerImpl handler, MinecraftServer server){ - if (this.server!=null && this.server != server){ - BCLib.LOGGER.warning("Server changed!"); - } - this.server = server; - for(DataHandlerDescriptor desc : getDescriptors()){ - ServerPlayNetworking.registerReceiver(handler, desc.IDENTIFIER, (_server, _player, _handler, _buf, _responseSender) -> { - receiveFromClient(desc, _server, _player, _handler, _buf, _responseSender); - }); - } - } - - public void onPlayReady(ServerGamePacketListenerImpl handler, PacketSender sender, MinecraftServer server){ - for(DataHandlerDescriptor desc : getDescriptors()){ - if (desc.sendOnJoin){ - BaseDataHandler h = desc.JOIN_INSTANCE.get(); - if (h.getOriginatesOnServer()) { - h.sendToClient(server, handler.player); - } - } - } - } - - public void onPlayDisconnect(ServerGamePacketListenerImpl handler, MinecraftServer server){ - for(DataHandlerDescriptor desc : getDescriptors()){ - ServerPlayNetworking.unregisterReceiver(handler, desc.IDENTIFIER); - } - } - - void receiveFromClient(DataHandlerDescriptor desc, MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender){ - BaseDataHandler h = desc.INSTANCE.get(); - h.receiveFromClient(server, player, handler, buf, responseSender); - } - - public void sendToClient(BaseDataHandler h){ - if (server==null){ - throw new RuntimeException("[internal error] Server not initialized yet!"); - } - h.sendToClient(this.server); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java b/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java deleted file mode 100644 index 868223c5..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java +++ /dev/null @@ -1,211 +0,0 @@ -package ru.bclib.api.dataexchange; - -import com.google.common.collect.Lists; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.network.FriendlyByteBuf; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.handler.DataExchange; -import ru.bclib.api.dataexchange.handler.autosync.AutoSync; -import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate; -import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID; -import ru.bclib.config.Config; -import ru.bclib.util.ModUtil; - -import java.io.File; -import java.util.List; -import java.util.function.BiConsumer; - -public class DataExchangeAPI extends DataExchange { - private final static List MODS = Lists.newArrayList(); - - /** - * You should never need to create a custom instance of this Object. - */ - public DataExchangeAPI() { - super(); - } - - @Environment(EnvType.CLIENT) - protected ConnectorClientside clientSupplier(DataExchange api) { - return new ConnectorClientside(api); - } - - protected ConnectorServerside serverSupplier(DataExchange api) { - return new ConnectorServerside(api); - } - - /** - * Register a mod to participate in the DataExchange. - * - * @param modID - {@link String} modID. - */ - public static void registerMod(String modID) { - if (!MODS.contains(modID)) MODS.add(modID); - } - - /** - * Register a mod dependency to participate in the DataExchange. - * - * @param modID - {@link String} modID. - */ - public static void registerModDependency(String modID) { - if (ModUtil.getModInfo(modID, false) != null && !"0.0.0".equals(ModUtil.getModVersion(modID))) { - registerMod(modID); - } else { - BCLib.LOGGER.info("Mod Dependency '" + modID + "' not found. This is probably OK."); - } - } - - /** - * Returns the IDs of all registered Mods. - * - * @return List of modIDs - */ - public static List registeredMods() { - return MODS; - } - - /** - * Add a new Descriptor for a {@link DataHandler}. - * - * @param desc The Descriptor you want to add. - */ - public static void registerDescriptor(DataHandlerDescriptor desc) { - DataExchange api = DataExchange.getInstance(); - api.getDescriptors() - .add(desc); - } - - /** - * Bulk-Add a Descriptors for your {@link DataHandler}-Objects. - * - * @param desc The Descriptors you want to add. - */ - public static void registerDescriptors(List desc) { - DataExchange api = DataExchange.getInstance(); - api.getDescriptors() - .addAll(desc); - } - - /** - * Sends the Handler. - *

- * Depending on what the result of {@link DataHandler#getOriginatesOnServer()}, the Data is sent from the server - * to the client (if {@code true}) or the other way around. - *

- * The method {@link DataHandler#serializeData(FriendlyByteBuf, boolean)} is called just before the data is sent. You should - * use this method to add the Data you need to the communication. - * - * @param h The Data that you want to send - */ - public static void send(BaseDataHandler h) { - if (h.getOriginatesOnServer()) { - DataExchangeAPI.getInstance().server.sendToClient(h); - } - else { - DataExchangeAPI.getInstance().client.sendToServer(h); - } - } - - /** - * Registers a File for automatic client syncing. - * - * @param modID The ID of the calling Mod - * @param fileName The name of the File - */ - public static void addAutoSyncFile(String modID, File fileName) { - AutoSync.addAutoSyncFileData(modID, fileName, false, SyncFileHash.NEED_TRANSFER); - } - - /** - * Registers a File for automatic client syncing. - *

- * The file is synced of the {@link SyncFileHash} on client and server are not equal. This method will not copy the - * configs content from the client to the server. - * - * @param modID The ID of the calling Mod - * @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for - * Details - * @param fileName The name of the File - */ - public static void addAutoSyncFile(String modID, String uniqueID, File fileName) { - AutoSync.addAutoSyncFileData(modID, uniqueID, fileName, false, SyncFileHash.NEED_TRANSFER); - } - - /** - * Registers a File for automatic client syncing. - *

- * The content of the file is requested for comparison. This will copy the - * entire file from the client to the server. - *

- * You should only use this option, if you need to compare parts of the file in order to decide - * if the File needs to be copied. Normally using the {@link SyncFileHash} - * for comparison is sufficient. - * - * @param modID The ID of the calling Mod - * @param fileName The name of the File - * @param needTransfer If the predicate returns true, the file needs to get copied to the server. - */ - public static void addAutoSyncFile(String modID, File fileName, NeedTransferPredicate needTransfer) { - AutoSync.addAutoSyncFileData(modID, fileName, true, needTransfer); - } - - /** - * Registers a File for automatic client syncing. - *

- * The content of the file is requested for comparison. This will copy the - * entire file from the client to the server. - *

- * You should only use this option, if you need to compare parts of the file in order to decide - * if the File needs to be copied. Normally using the {@link SyncFileHash} - * for comparison is sufficient. - * - * @param modID The ID of the calling Mod - * @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for - * Details - * @param fileName The name of the File - * @param needTransfer If the predicate returns true, the file needs to get copied to the server. - */ - public static void addAutoSyncFile(String modID, String uniqueID, File fileName, NeedTransferPredicate needTransfer) { - AutoSync.addAutoSyncFileData(modID, uniqueID, fileName, true, needTransfer); - } - - /** - * Register a function that is called whenever the client receives a file from the server and replaced toe local - * file with the new content. - *

- * This callback is usefull if you need to reload the new content before the game is quit. - * - * @param callback A Function that receives the AutoSyncID as well as the Filename. - */ - public static void addOnWriteCallback(BiConsumer callback) { - AutoSync.addOnWriteCallback(callback); - } - - /** - * Returns the sync-folder for a given Mod. - *

- * BCLib will ensure that the contents of sync-folder on the client is the same as the one on the server. - * - * @param modID ID of the Mod - * @return The path to the sync-folder - */ - public static File getModSyncFolder(String modID) { - File fl = AutoSync.SYNC_FOLDER.localFolder.resolve(modID.replace(".", "-") - .replace(":", "-") - .replace("\\", "-") - .replace("/", "-")) - .normalize() - .toFile(); - - if (!fl.exists()) { - fl.mkdirs(); - } - return fl; - } - - static { - addOnWriteCallback(Config::reloadSyncedConfig); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/DataHandler.java b/src/main/java/ru/bclib/api/dataexchange/DataHandler.java deleted file mode 100644 index 30492bed..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/DataHandler.java +++ /dev/null @@ -1,273 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.fabricmc.fabric.api.networking.v1.PlayerLookup; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.entity.player.Player; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.handler.autosync.Chunker; -import ru.bclib.api.dataexchange.handler.autosync.Chunker.PacketChunkSender; - -import java.util.Collection; -import java.util.List; - -public abstract class DataHandler extends BaseDataHandler { - public abstract static class WithoutPayload extends DataHandler { - protected WithoutPayload(ResourceLocation identifier, boolean originatesOnServer) { - super(identifier, originatesOnServer); - } - - @Override - protected boolean prepareData(boolean isClient) { return true; } - - @Override - protected void serializeData(FriendlyByteBuf buf, boolean isClient) { - } - - @Override - protected void deserializeIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean isClient) { - } - } - - protected DataHandler(ResourceLocation identifier, boolean originatesOnServer) { - super(identifier, originatesOnServer); - } - - protected boolean prepareData(boolean isClient) { return true; } - - abstract protected void serializeData(FriendlyByteBuf buf, boolean isClient); - - abstract protected void deserializeIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean isClient); - - abstract protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient); - - - @Environment(EnvType.CLIENT) - @Override - void receiveFromServer(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender) { - deserializeIncomingData(buf, responseSender, true); - final Runnable runner = () -> runOnGameThread(client, null, true); - - if (isBlocking()) client.executeBlocking(runner); - else client.execute(runner); - } - - @Override - void receiveFromClient(MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender) { - super.receiveFromClient(server, player, handler, buf, responseSender); - - deserializeIncomingData(buf, responseSender, false); - final Runnable runner = () -> runOnGameThread(null, server, false); - - if (isBlocking()) server.executeBlocking(runner); - else server.execute(runner); - } - - @Override - void sendToClient(MinecraftServer server) { - if (prepareData(false)) { - FriendlyByteBuf buf = PacketByteBufs.create(); - serializeData(buf, false); - - _sendToClient(getIdentifier(), server, PlayerLookup.all(server), buf); - } - } - - @Override - void sendToClient(MinecraftServer server, ServerPlayer player) { - if (prepareData(false)) { - FriendlyByteBuf buf = PacketByteBufs.create(); - serializeData(buf, false); - - _sendToClient(getIdentifier(), server, List.of(player), buf); - } - } - - - public static void _sendToClient(ResourceLocation identifier, MinecraftServer server, Collection players, FriendlyByteBuf buf) { - if (buf.readableBytes()> Chunker.MAX_PACKET_SIZE) { - final PacketChunkSender sender = new PacketChunkSender(buf, identifier); - sender.sendChunks(players); - } else { - for (ServerPlayer player : players) { - ServerPlayNetworking.send(player, identifier, buf); - } - } - } - - @Environment(EnvType.CLIENT) - @Override - void sendToServer(Minecraft client) { - if (prepareData(true)) { - FriendlyByteBuf buf = PacketByteBufs.create(); - serializeData(buf, true); - ClientPlayNetworking.send(getIdentifier(), buf); - } - } - - /** - * A Message that always originates on the Client - */ - public abstract static class FromClient extends BaseDataHandler { - public abstract static class WithoutPayload extends FromClient { - protected WithoutPayload(ResourceLocation identifier) { - super(identifier); - } - - @Override - protected boolean prepareDataOnClient() { return true; } - - @Override - protected void serializeDataOnClient(FriendlyByteBuf buf) { - } - - @Override - protected void deserializeIncomingDataOnServer(FriendlyByteBuf buf, Player player, PacketSender responseSender) { - } - } - - protected FromClient(ResourceLocation identifier) { - super(identifier, false); - } - - @Environment(EnvType.CLIENT) - protected boolean prepareDataOnClient() { return true; } - - @Environment(EnvType.CLIENT) - abstract protected void serializeDataOnClient(FriendlyByteBuf buf); - - protected abstract void deserializeIncomingDataOnServer(FriendlyByteBuf buf, Player player, PacketSender responseSender); - protected abstract void runOnServerGameThread(MinecraftServer server, Player player); - - @Environment(EnvType.CLIENT) - @Override - void receiveFromServer(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender) { - BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the client!"); - } - - @Override - void receiveFromClient(MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender) { - super.receiveFromClient(server, player, handler, buf, responseSender); - - deserializeIncomingDataOnServer(buf, player, responseSender); - final Runnable runner = () -> runOnServerGameThread(server, player); - - if (isBlocking()) server.executeBlocking(runner); - else server.execute(runner); - } - - @Override - void sendToClient(MinecraftServer server) { - BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the client!"); - } - - @Override - void sendToClient(MinecraftServer server, ServerPlayer player) { - BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the client!"); - } - - @Environment(EnvType.CLIENT) - @Override - void sendToServer(Minecraft client) { - if (prepareDataOnClient()) { - FriendlyByteBuf buf = PacketByteBufs.create(); - serializeDataOnClient(buf); - ClientPlayNetworking.send(getIdentifier(), buf); - } - } - } - - /** - * A Message that always originates on the Server - */ - public abstract static class FromServer extends BaseDataHandler { - public abstract static class WithoutPayload extends FromServer { - protected WithoutPayload(ResourceLocation identifier) { - super(identifier); - } - - @Override - protected boolean prepareDataOnServer() { return true; } - - @Override - protected void serializeDataOnServer(FriendlyByteBuf buf) { - } - - @Override - protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { - } - } - - protected FromServer(ResourceLocation identifier) { - super(identifier, true); - } - - protected boolean prepareDataOnServer() { return true; } - - abstract protected void serializeDataOnServer(FriendlyByteBuf buf); - - @Environment(EnvType.CLIENT) - abstract protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender); - - @Environment(EnvType.CLIENT) - abstract protected void runOnClientGameThread(Minecraft client); - - - @Environment(EnvType.CLIENT) - @Override - final void receiveFromServer(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender) { - deserializeIncomingDataOnClient(buf, responseSender); - final Runnable runner = () -> runOnClientGameThread(client); - - if (isBlocking()) client.executeBlocking(runner); - else client.execute(runner); - } - - @Override - final void receiveFromClient(MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender) { - super.receiveFromClient(server, player, handler, buf, responseSender); - BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the server!"); - } - - public void receiveFromMemory(FriendlyByteBuf buf){ - receiveFromServer(Minecraft.getInstance(), null, buf, null); - } - - @Override - final void sendToClient(MinecraftServer server) { - if (prepareDataOnServer()) { - FriendlyByteBuf buf = PacketByteBufs.create(); - serializeDataOnServer(buf); - - _sendToClient(getIdentifier(), server, PlayerLookup.all(server), buf); - } - } - - @Override - final void sendToClient(MinecraftServer server, ServerPlayer player) { - if (prepareDataOnServer()) { - FriendlyByteBuf buf = PacketByteBufs.create(); - serializeDataOnServer(buf); - - _sendToClient(getIdentifier(), server, List.of(player), buf); - } - } - - @Environment(EnvType.CLIENT) - @Override - final void sendToServer(Minecraft client) { - BCLib.LOGGER.error("[Internal Error] The message '" + getIdentifier() + "' must originate from the server!"); - } - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/DataHandlerDescriptor.java b/src/main/java/ru/bclib/api/dataexchange/DataHandlerDescriptor.java deleted file mode 100644 index 2f9f80e5..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/DataHandlerDescriptor.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; -import java.util.function.Supplier; - -public class DataHandlerDescriptor { - public DataHandlerDescriptor(@NotNull ResourceLocation identifier, @NotNull Supplier instancer){ - this(identifier, instancer, instancer, false, false); - } - - public DataHandlerDescriptor(@NotNull ResourceLocation identifier,@NotNull Supplier instancer, boolean sendOnJoin, boolean sendBeforeEnter){ - this(identifier, instancer, instancer, sendOnJoin, sendBeforeEnter); - } - public DataHandlerDescriptor(@NotNull ResourceLocation identifier, @NotNull Supplier receiv_instancer, @NotNull Supplier join_instancer, boolean sendOnJoin, boolean sendBeforeEnter){ - this.INSTANCE = receiv_instancer; - this.JOIN_INSTANCE = join_instancer; - this.IDENTIFIER = identifier; - - this.sendOnJoin = sendOnJoin; - this.sendBeforeEnter = sendBeforeEnter; - } - - public final boolean sendOnJoin; - public final boolean sendBeforeEnter; - @NotNull - public final ResourceLocation IDENTIFIER; - @NotNull - public final Supplier INSTANCE; - @NotNull - public final Supplier JOIN_INSTANCE; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o instanceof ResourceLocation){ - return o.equals(IDENTIFIER); - } - if (!(o instanceof DataHandlerDescriptor that)) return false; - return IDENTIFIER.equals(that.IDENTIFIER); - } - - @Override - public int hashCode() { - return Objects.hash(IDENTIFIER); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/FileHash.java b/src/main/java/ru/bclib/api/dataexchange/FileHash.java deleted file mode 100644 index de87f686..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/FileHash.java +++ /dev/null @@ -1,161 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.minecraft.network.FriendlyByteBuf; -import org.jetbrains.annotations.NotNull; -import ru.bclib.BCLib; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Objects; - -public class FileHash { - private static int ERR_DOES_NOT_EXIST = -10; - private static int ERR_IO_ERROR = -20; - - /** - * The md5-hash of the file - */ - @NotNull - public final byte[] md5; - - /** - * The size (in bytes) of the input. - */ - public final int size; - - /** - * a value that is directly calculated from defined byte positions. - */ - public final int value; - - FileHash(byte[] md5, int size, int value) { - Objects.nonNull(md5); - - this.md5 = md5; - this.size = size; - this.value = value; - } - - static FileHash createForEmpty(int errCode) { - return new FileHash(new byte[0], 0, errCode); - } - - public boolean noFile() { - return md5.length == 0; - } - - /** - * Serializes the Object to a buffer - * - * @param buf The buffer to write to - */ - public void serialize(FriendlyByteBuf buf) { - buf.writeInt(size); - buf.writeInt(value); - buf.writeByteArray(md5); - } - - /** - * Deserialize a Buffer to a new {@link SyncFileHash}-Object - * - * @param buf Thea buffer to read from - * @return The received String - */ - public static FileHash deserialize(FriendlyByteBuf buf) { - final int size = buf.readInt(); - final int value = buf.readInt(); - final byte[] md5 = buf.readByteArray(); - - return new FileHash(md5, size, value); - } - - /** - * Convert the md5-hash to a human readable string - * - * @return The converted String - */ - public String getMd5String() { - return toHexString(md5); - } - - /** - * Converts a byte-array to a hex-string representation - * - * @param bytes The source array - * @return The resulting string, or an empty String if the input was {@code null} - */ - public static String toHexString(byte[] bytes) { - if (bytes == null) return ""; - - StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } - - /** - * Create a new {@link FileHash}. - * - * @param file The input file - * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are - * identical. Will return {@code null} when an error occurs or the File does not exist - */ - public static FileHash create(File file) { - if (!file.exists()) return createForEmpty(ERR_DOES_NOT_EXIST); - final Path path = file.toPath(); - - int size = 0; - byte[] md5 = new byte[0]; - int value = 0; - - try { - byte[] data = Files.readAllBytes(path); - - size = data.length; - - value = size > 0 ? (data[size / 3] | (data[size / 2] << 8) | (data[size / 5] << 16)) : -1; - if (size > 20) value |= data[20] << 24; - - MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(data); - md5 = md.digest(); - - return new FileHash(md5, size, value); - } - catch (IOException e) { - BCLib.LOGGER.error("Failed to read file: " + file); - return null; - } - catch (NoSuchAlgorithmException e) { - BCLib.LOGGER.error("Unable to build hash for file: " + file); - } - - return createForEmpty(ERR_IO_ERROR); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof FileHash)) return false; - FileHash fileHash = (FileHash) o; - return size == fileHash.size && value == fileHash.value && Arrays.equals(md5, fileHash.md5); - } - - @Override - public int hashCode() { - int result = Objects.hash(size, value); - result = 31 * result + Arrays.hashCode(md5); - return result; - } - - @Override - public String toString() { - return String.format("%08x", size) + "-" + String.format("%08x", value) + "-" + getMd5String(); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/SyncFileHash.java b/src/main/java/ru/bclib/api/dataexchange/SyncFileHash.java deleted file mode 100644 index d8bc1e6a..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/SyncFileHash.java +++ /dev/null @@ -1,108 +0,0 @@ -package ru.bclib.api.dataexchange; - -import net.minecraft.network.FriendlyByteBuf; -import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate; -import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID; - -import java.io.File; -import java.util.Objects; - -/** - * Calculates a hash based on the contents of a File. - *

- * A File-Hash contains the md5-sum of the File, as well as its size and byte-values from defined positions - *

- * You can compare instances using {@link #equals(Object)} to determine if two files are - * identical. - */ -public class SyncFileHash extends AutoSyncID { - public final FileHash hash; - - SyncFileHash(String modID, File file, byte[] md5, int size, int value) { - this(modID, file.getName(), md5, size, value); - } - - SyncFileHash(String modID, String uniqueID, byte[] md5, int size, int value) { - this(modID, uniqueID, new FileHash(md5, size, value)); - } - - SyncFileHash(String modID, File file, FileHash hash) { - this(modID, file.getName(), hash); - } - - SyncFileHash(String modID, String uniqueID, FileHash hash) { - super(modID, uniqueID); - this.hash = hash; - } - - - final static NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content)-> !clientHash.equals(serverHash); - - @Override - public String toString() { - return super.toString()+": "+hash.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof SyncFileHash)) return false; - if (!super.equals(o)) return false; - SyncFileHash that = (SyncFileHash) o; - return hash.equals(that.hash); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), hash); - } - - /** - * Serializes the Object to a buffer - * @param buf The buffer to write to - */ - public void serialize(FriendlyByteBuf buf) { - hash.serialize(buf); - DataHandler.writeString(buf, modID); - DataHandler.writeString(buf, uniqueID); - } - - /** - *Deserialize a Buffer to a new {@link SyncFileHash}-Object - * @param buf Thea buffer to read from - * @return The received String - */ - public static SyncFileHash deserialize(FriendlyByteBuf buf){ - final FileHash hash = FileHash.deserialize(buf); - final String modID = DataHandler.readString(buf); - final String uniqueID = DataHandler.readString(buf); - - return new SyncFileHash(modID, uniqueID, hash); - } - - /** - * Create a new {@link SyncFileHash}. - *

- * Will call {@link #create(String, File, String)} using the name of the File as {@code uniqueID}. - * @param modID ID of the calling Mod - * @param file The input file - * - * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are - * identical. Will return {@code null} when an error occurs or the File does not exist - */ - public static SyncFileHash create(String modID, File file){ - return create(modID, file, file.getName()); - } - - /** - * Create a new {@link SyncFileHash}. - * @param modID ID of the calling Mod - * @param file The input file - * @param uniqueID The unique ID that is used for this File (see {@link SyncFileHash#uniqueID} for Details. - * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are - * identical. Will return {@code null} when an error occurs or the File does not exist - */ - public static SyncFileHash create(String modID, File file, String uniqueID){ - return new SyncFileHash(modID, uniqueID, FileHash.create(file)); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java b/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java deleted file mode 100644 index a11ddb6d..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java +++ /dev/null @@ -1,113 +0,0 @@ -package ru.bclib.api.dataexchange.handler; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.api.dataexchange.BaseDataHandler; -import ru.bclib.api.dataexchange.ConnectorClientside; -import ru.bclib.api.dataexchange.ConnectorServerside; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.DataHandlerDescriptor; - -import java.util.HashSet; -import java.util.Set; - -abstract public class DataExchange { - - - private static DataExchangeAPI instance; - - protected static DataExchangeAPI getInstance() { - if (instance == null) { - instance = new DataExchangeAPI(); - } - return instance; - } - - protected ConnectorServerside server; - protected ConnectorClientside client; - protected final Set descriptors; - - - private boolean didLoadSyncFolder = false; - - abstract protected ConnectorClientside clientSupplier(DataExchange api); - - abstract protected ConnectorServerside serverSupplier(DataExchange api); - - protected DataExchange() { - descriptors = new HashSet<>(); - } - - public Set getDescriptors() { return descriptors; } - - public static DataHandlerDescriptor getDescriptor(ResourceLocation identifier){ - return getInstance().descriptors.stream().filter(d -> d.equals(identifier)).findFirst().orElse(null); - } - - @Environment(EnvType.CLIENT) - protected void initClientside() { - if (client != null) return; - client = clientSupplier(this); - - ClientPlayConnectionEvents.INIT.register(client::onPlayInit); - ClientPlayConnectionEvents.JOIN.register(client::onPlayReady); - ClientPlayConnectionEvents.DISCONNECT.register(client::onPlayDisconnect); - } - - protected void initServerSide() { - if (server != null) return; - server = serverSupplier(this); - - ServerPlayConnectionEvents.INIT.register(server::onPlayInit); - ServerPlayConnectionEvents.JOIN.register(server::onPlayReady); - ServerPlayConnectionEvents.DISCONNECT.register(server::onPlayDisconnect); - } - - /** - * Initializes all datastructures that need to exist in the client component. - *

- * This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called - */ - @Environment(EnvType.CLIENT) - public static void prepareClientside() { - DataExchange api = DataExchange.getInstance(); - api.initClientside(); - - } - - /** - * Initializes all datastructures that need to exist in the server component. - *

- * This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called - */ - public static void prepareServerside() { - DataExchange api = DataExchange.getInstance(); - api.initServerSide(); - } - - - /** - * Automatically called before the player enters the world. - *

- * This is automatically called by BCLib. It will send all {@link DataHandler}-Objects that have {@link DataHandlerDescriptor#sendBeforeEnter} set to* - * {@code true}, - */ - @Environment(EnvType.CLIENT) - public static void sendOnEnter() { - getInstance().descriptors.forEach((desc) -> { - if (desc.sendBeforeEnter) { - BaseDataHandler h = desc.JOIN_INSTANCE.get(); - if (!h.getOriginatesOnServer()) { - getInstance().client.sendToServer(h); - } - } - }); - } - - - -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java deleted file mode 100644 index 83e4ff0b..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java +++ /dev/null @@ -1,237 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.minecraft.network.FriendlyByteBuf; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.SyncFileHash; -import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate; -import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile; -import ru.bclib.util.ModUtil; -import ru.bclib.util.ModUtil.ModInfo; -import ru.bclib.util.Pair; -import ru.bclib.util.PathUtil; -import ru.bclib.util.Triple; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -class AutoFileSyncEntry extends AutoSyncID { - static class ForDirectFileRequest extends AutoFileSyncEntry { - final File relFile; - - ForDirectFileRequest(String syncID, File relFile, File absFile) { - super(AutoSyncID.ForDirectFileRequest.MOD_ID, syncID, absFile, false, (a, b, c) -> false); - this.relFile = relFile; - } - - @Override - public int serializeContent(FriendlyByteBuf buf) { - int res = super.serializeContent(buf); - DataHandler.writeString(buf, relFile.toString()); - - return res; - } - - static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf) { - final String relFile = DataHandler.readString(buf); - SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(syncID); - if (desc != null) { - //ensures that the file is not above the base-folder - if (desc.acceptChildElements(desc.mapAbsolute(relFile))) { - return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile) - .normalize() - .toFile()); - } - } - return null; - } - - @Override - public String toString() { - return uniqueID + " - " + relFile; - } - } - - static class ForModFileRequest extends AutoFileSyncEntry { - public static File getLocalPathForID(String modID, boolean matchLocalVersion){ - ModInfo mi = ModUtil.getModInfo(modID, matchLocalVersion); - if (mi!=null){ - return mi.jarPath.toFile(); - } - return null; - } - - public final String version; - ForModFileRequest(String modID, boolean matchLocalVersion, String version) { - super(modID, AutoSyncID.ForModFileRequest.UNIQUE_ID, getLocalPathForID(modID, matchLocalVersion), false, (a, b, c) -> false); - if (this.fileName == null && matchLocalVersion){ - BCLib.LOGGER.error("Unknown mod '"+modID+"'."); - } - if (version==null) - this.version = ModUtil.getModVersion(modID); - else - this.version = version; - } - - @Override - public int serializeContent(FriendlyByteBuf buf) { - final int res = super.serializeContent(buf); - buf.writeInt(ModUtil.convertModVersion(version)); - return res; - } - - static AutoFileSyncEntry.ForModFileRequest finishDeserializeContent(String modID, FriendlyByteBuf buf) { - final String version = ModUtil.convertModVersion(buf.readInt()); - return new AutoFileSyncEntry.ForModFileRequest(modID, false, version); - } - - @Override - public String toString() { - return "Mod " + modID + " (v" + version + ")"; - } - } - - public final NeedTransferPredicate needTransfer; - public final File fileName; - public final boolean requestContent; - private SyncFileHash hash; - - AutoFileSyncEntry(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) { - this(modID, fileName.getName(), fileName, requestContent, needTransfer); - } - - AutoFileSyncEntry(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) { - super(modID, uniqueID); - this.needTransfer = needTransfer; - this.fileName = fileName; - this.requestContent = requestContent; - } - - - public SyncFileHash getFileHash() { - if (hash == null) { - hash = SyncFileHash.create(modID, fileName, uniqueID); - } - return hash; - } - - public byte[] getContent() { - if (!fileName.exists()) return new byte[0]; - final Path path = fileName.toPath(); - - try { - return Files.readAllBytes(path); - } - catch (IOException e) { - - } - return new byte[0]; - } - - public int serializeContent(FriendlyByteBuf buf) { - DataHandler.writeString(buf, modID); - DataHandler.writeString(buf, uniqueID); - return serializeFileContent(buf); - } - - public static Triple deserializeContent(FriendlyByteBuf buf) { - final String modID = DataHandler.readString(buf); - final String uniqueID = DataHandler.readString(buf); - byte[] data = deserializeFileContent(buf); - - AutoFileSyncEntry entry; - if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)) { - entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf); - } - else if (AutoSyncID.ForModFileRequest.UNIQUE_ID.equals(uniqueID)) { - entry = AutoFileSyncEntry.ForModFileRequest.finishDeserializeContent(modID, buf); - } - else { - entry = AutoFileSyncEntry.findMatching(modID, uniqueID); - } - return new Triple<>(entry, data, new AutoSyncID(modID, uniqueID)); - } - - - public void serialize(FriendlyByteBuf buf) { - getFileHash().serialize(buf); - buf.writeBoolean(requestContent); - - if (requestContent) { - serializeFileContent(buf); - } - } - - public static AutoSync.AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf) { - Pair e = deserialize(buf); - AutoFileSyncEntry match = findMatching(e.first); - return new AutoSync.AutoSyncTriple(e.first, e.second, match); - } - - public static Pair deserialize(FriendlyByteBuf buf) { - SyncFileHash hash = SyncFileHash.deserialize(buf); - boolean withContent = buf.readBoolean(); - byte[] data = null; - if (withContent) { - data = deserializeFileContent(buf); - } - - return new Pair(hash, data); - } - - private int serializeFileContent(FriendlyByteBuf buf) { - if (!PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())){ - BCLib.LOGGER.error(fileName + " is not within game folder " + PathUtil.GAME_FOLDER + ". Pretending it does not exist."); - buf.writeInt(0); - return 0; - } - - byte[] content = getContent(); - buf.writeInt(content.length); - buf.writeByteArray(content); - return content.length; - } - - private static byte[] deserializeFileContent(FriendlyByteBuf buf) { - byte[] data; - int size = buf.readInt(); - data = buf.readByteArray(size); - return data; - } - - - public static AutoFileSyncEntry findMatching(SyncFileHash hash) { - return findMatching(hash.modID, hash.uniqueID); - } - - public static AutoFileSyncEntry findMatching(AutoSyncID aid) { - if (aid instanceof AutoSyncID.ForDirectFileRequest) { - AutoSyncID.ForDirectFileRequest freq = (AutoSyncID.ForDirectFileRequest) aid; - SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(freq.uniqueID); - if (desc != null) { - SubFile subFile = desc.getLocalSubFile(freq.relFile.toString()); - if (subFile != null) { - final File absPath = desc.localFolder.resolve(subFile.relPath) - .normalize() - .toFile(); - return new AutoFileSyncEntry.ForDirectFileRequest(freq.uniqueID, new File(subFile.relPath), absPath); - } - } - return null; - } else if (aid instanceof AutoSyncID.ForModFileRequest) { - AutoSyncID.ForModFileRequest mreq = (AutoSyncID.ForModFileRequest) aid; - return new AutoFileSyncEntry.ForModFileRequest(mreq.modID, true, null); - } - return findMatching(aid.modID, aid.uniqueID); - } - - public static AutoFileSyncEntry findMatching(String modID, String uniqueID) { - return AutoSync.getAutoSyncFiles() - .stream() - .filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID)) - .findFirst() - .orElse(null); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java deleted file mode 100644 index eace188d..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java +++ /dev/null @@ -1,187 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.fabricmc.loader.api.FabricLoader; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.dataexchange.SyncFileHash; -import ru.bclib.config.Configs; -import ru.bclib.config.ServerConfig; -import ru.bclib.util.PathUtil; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.BiConsumer; - -public class AutoSync { - public static final String SYNC_CATEGORY = "auto_sync"; - public final static SyncFolderDescriptor SYNC_FOLDER = new SyncFolderDescriptor("BCLIB-SYNC", FabricLoader.getInstance() - .getGameDir() - .resolve("bclib-sync") - .normalize() - .toAbsolutePath(), true); - - @FunctionalInterface - public interface NeedTransferPredicate { - public boolean test(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content); - } - - final static class AutoSyncTriple { - public final SyncFileHash serverHash; - public final byte[] serverContent; - public final AutoFileSyncEntry localMatch; - - public AutoSyncTriple(SyncFileHash serverHash, byte[] serverContent, AutoFileSyncEntry localMatch) { - this.serverHash = serverHash; - this.serverContent = serverContent; - this.localMatch = localMatch; - } - - @Override - public String toString() { - return serverHash.modID + "." + serverHash.uniqueID; - } - } - - - // ##### File Syncing - protected final static List> onWriteCallbacks = new ArrayList<>(2); - /** - * Register a function that is called whenever the client receives a file from the server and replaced toe local - * file with the new content. - *

- * This callback is usefull if you need to reload the new content before the game is quit. - * - * @param callback A Function that receives the AutoSyncID as well as the Filename. - */ - public static void addOnWriteCallback(BiConsumer callback) { - onWriteCallbacks.add(callback); - } - private static final List autoSyncFiles = new ArrayList<>(4); - - public static List getAutoSyncFiles() { - return autoSyncFiles; - } - - /** - * Registers a File for automatic client syncing. - * - * @param modID The ID of the calling Mod - * @param needTransfer If the predicate returns true, the file needs to get copied to the server. - * @param fileName The name of the File - * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the - * entire file from the client to the server. - *

- * You should only use this option, if you need to compare parts of the file in order to decide - * If the File needs to be copied. Normally using the {@link SyncFileHash} - * for comparison is sufficient. - */ - public static void addAutoSyncFileData(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) { - if (!PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())){ - BCLib.LOGGER.error(fileName + " is outside of Game Folder " + PathUtil.GAME_FOLDER); - } else { - autoSyncFiles.add(new AutoFileSyncEntry(modID, fileName, requestContent, needTransfer)); - } - } - - /** - * Registers a File for automatic client syncing. - * - * @param modID The ID of the calling Mod - * @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for - * Details - * @param needTransfer If the predicate returns true, the file needs to get copied to the server. - * @param fileName The name of the File - * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the - * entire file from the client to the server. - *

- * You should only use this option, if you need to compare parts of the file in order to decide - * If the File needs to be copied. Normally using the {@link SyncFileHash} - * for comparison is sufficient. - */ - public static void addAutoSyncFileData(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) { - if (!PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())){ - BCLib.LOGGER.error(fileName + " is outside of Game Folder " + PathUtil.GAME_FOLDER); - } else { - autoSyncFiles.add(new AutoFileSyncEntry(modID, uniqueID, fileName, requestContent, needTransfer)); - } - } - - /** - * Called when {@code SendFiles} received a File on the Client and wrote it to the FileSystem. - *

- * This is the place where reload Code should go. - * - * @param aid The ID of the received File - * @param file The location of the FIle on the client - */ - static void didReceiveFile(AutoSyncID aid, File file) { - onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file)); - } - - - // ##### Folder Syncing - static final List syncFolderDescriptions = Arrays.asList(SYNC_FOLDER); - - private List syncFolderContent; - - protected List getSyncFolderContent() { - if (syncFolderContent == null) { - return new ArrayList<>(0); - } - return syncFolderContent; - } - - private static boolean didRegisterAdditionalMods = false; - //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()); - } - - if (!didRegisterAdditionalMods && Configs.SERVER_CONFIG.isOfferingMods()){ - didRegisterAdditionalMods = true; - List modIDs = Configs.SERVER_CONFIG.get(ServerConfig.ADDITIONAL_MODS); - if (modIDs != null){ - modIDs.stream().forEach(modID -> DataExchangeAPI.registerModDependency(modID)); - } - } - - } - - protected static SyncFolderDescriptor getSyncFolderDescriptor(String folderID) { - return syncFolderDescriptions.stream() - .filter(d -> d.equals(folderID)) - .findFirst() - .orElse(null); - } - - protected static Path localBasePathForFolderID(String folderID) { - final SyncFolderDescriptor desc = getSyncFolderDescriptor(folderID); - if (desc != null) { - return desc.localFolder; - } - else { - BCLib.LOGGER.warning("Unknown Sync-Folder ID '" + folderID + "'"); - return null; - } - } - - public static void registerSyncFolder(String folderID, Path localBaseFolder, boolean removeAdditionalFiles) { - localBaseFolder = localBaseFolder.normalize(); - if (PathUtil.isChildOf(PathUtil.GAME_FOLDER, localBaseFolder)) { - final SyncFolderDescriptor desc = new SyncFolderDescriptor(folderID, localBaseFolder, removeAdditionalFiles); - if (syncFolderDescriptions.contains(desc)) { - BCLib.LOGGER.warning("Tried to override Folder Sync '" + folderID + "' again."); - } - else { - syncFolderDescriptions.add(desc); - } - } - else { - BCLib.LOGGER.error(localBaseFolder + " (from " + folderID + ") is outside the game directory " + PathUtil.GAME_FOLDER + ". Sync is not allowed."); - } - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java deleted file mode 100644 index 9ffbc64f..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java +++ /dev/null @@ -1,142 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.minecraft.network.FriendlyByteBuf; -import org.jetbrains.annotations.NotNull; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.config.Config; -import ru.bclib.util.ModUtil; - -import java.io.File; -import java.util.Objects; - -public class AutoSyncID { - static class WithContentOverride extends AutoSyncID { - final FileContentWrapper contentWrapper; - final File localFile; - - WithContentOverride(String modID, String uniqueID, FileContentWrapper contentWrapper, File localFile) { - super(modID, uniqueID); - this.contentWrapper = contentWrapper; - this.localFile = localFile; - } - - @Override - public String toString() { - return super.toString() + " (Content override)"; - } - } - - static class ForDirectFileRequest extends AutoSyncID { - public final static String MOD_ID = "bclib::FILE"; - final File relFile; - - ForDirectFileRequest(String syncID, File relFile) { - super(ForDirectFileRequest.MOD_ID, syncID); - this.relFile = relFile; - } - - @Override - void serializeData(FriendlyByteBuf buf) { - super.serializeData(buf); - DataHandler.writeString(buf, relFile.toString()); - } - - static ForDirectFileRequest finishDeserialize(String modID, String uniqueID, FriendlyByteBuf buf){ - final File fl = new File(DataHandler.readString(buf)); - return new ForDirectFileRequest(uniqueID, fl); - } - - @Override - public String toString() { - return super.uniqueID + " (" + this.relFile + ")"; - } - } - - static class ForModFileRequest extends AutoSyncID { - public final static String UNIQUE_ID = "bclib::MOD"; - private final String version; - ForModFileRequest(String modID, String version) { - super(modID, ForModFileRequest.UNIQUE_ID); - this.version = version; - } - - @Override - void serializeData(FriendlyByteBuf buf) { - super.serializeData(buf); - buf.writeInt(ModUtil.convertModVersion(version)); - } - - static ForModFileRequest finishDeserialize(String modID, String uniqueID, FriendlyByteBuf buf){ - final String version = ModUtil.convertModVersion(buf.readInt()); - return new ForModFileRequest(modID, version); - } - - @Override - public String toString() { - return super.modID + " (v" + this.version + ")"; - } - } - - /** - * A Unique ID for the referenced File. - *

- * Files with the same {@link #modID} need to have a unique IDs. Normally the filename from FileHash(String, File, byte[], int, int) - * is used to generated that ID, but you can directly specify one using FileHash(String, String, byte[], int, int). - */ - @NotNull - public final String uniqueID; - - /** - * The ID of the Mod that is registering the File - */ - @NotNull - public final String modID; - - public AutoSyncID(String modID, String uniqueID) { - Objects.nonNull(modID); - Objects.nonNull(uniqueID); - - this.modID = modID; - this.uniqueID = uniqueID; - } - - @Override - public String toString() { - return modID + "." + uniqueID; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof AutoSyncID)) return false; - AutoSyncID that = (AutoSyncID) o; - return uniqueID.equals(that.uniqueID) && modID.equals(that.modID); - } - - @Override - public int hashCode() { - return Objects.hash(uniqueID, modID); - } - - void serializeData(FriendlyByteBuf buf) { - DataHandler.writeString(buf, modID); - DataHandler.writeString(buf, uniqueID); - } - - static AutoSyncID deserializeData(FriendlyByteBuf buf){ - String modID = DataHandler.readString(buf); - String uID = DataHandler.readString(buf); - - if (ForDirectFileRequest.MOD_ID.equals(modID)){ - return ForDirectFileRequest.finishDeserialize(modID, uID, buf); - } else if (ForModFileRequest.UNIQUE_ID.equals(uID)){ - return ForModFileRequest.finishDeserialize(modID, uID, buf); - } else{ - return new AutoSyncID(modID, uID); - } - } - - public boolean isConfigFile(){ - return this.uniqueID.startsWith(Config.CONFIG_SYNC_PREFIX); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/Chunker.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/Chunker.java deleted file mode 100644 index bc669f3b..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/Chunker.java +++ /dev/null @@ -1,270 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -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.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; -import ru.bclib.api.dataexchange.BaseDataHandler; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.DataHandlerDescriptor; -import ru.bclib.api.dataexchange.handler.DataExchange; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - -/** - * Used to seperate large data transfers into multiple smaller messages. - *

- * {@link DataHandler} will automatically convert larger messages into Chunks on the Server - * and assemble the original message from those chunks on the client. - */ -public class Chunker extends DataHandler.FromServer { - - /** - * Responsible for assembling the original ByteBuffer created by {@link PacketChunkSender} on the - * receiving end. Automatically created from the header {@link Chunker}-Message (where the serialNo==-1) - */ - static class PacketChunkReceiver { - @NotNull - public final UUID uuid; - public final int chunkCount; - @NotNull - private final FriendlyByteBuf networkedBuf; - @Nullable - private final DataHandlerDescriptor descriptor; - - private static List active = new ArrayList<>(1); - private static PacketChunkReceiver newReceiver(@NotNull UUID uuid, int chunkCount, ResourceLocation origin){ - DataHandlerDescriptor desc = DataExchange.getDescriptor(origin); - final PacketChunkReceiver r = new PacketChunkReceiver(uuid, chunkCount, desc); - active.add(r); - return r; - } - - private static PacketChunkReceiver getOrCreate(@NotNull UUID uuid, int chunkCount, ResourceLocation origin){ - return active.stream().filter(r -> r.uuid.equals(uuid)).findFirst().orElse(newReceiver(uuid, chunkCount, origin)); - } - - public static PacketChunkReceiver get(@NotNull UUID uuid){ - return active.stream().filter(r -> r.uuid.equals(uuid)).findFirst().orElse(null); - } - - private PacketChunkReceiver(@NotNull UUID uuid, int chunkCount, @Nullable DataHandlerDescriptor descriptor){ - this.uuid = uuid; - this.chunkCount = chunkCount; - networkedBuf = PacketByteBufs.create(); - this.descriptor = descriptor; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof PacketChunkReceiver)) return false; - PacketChunkReceiver that = (PacketChunkReceiver) o; - return uuid.equals(that.uuid); - } - - @Override - public int hashCode() { - return Objects.hash(uuid); - } - - public boolean testFinished(){ - ProgressListener listener = ChunkerProgress.getProgressListener(); - if (listener!=null){ - listener.progressStagePercentage((100*receivedCount)/chunkCount); - } - if (incomingBuffer == null){ - return true; - } if (lastReadSerial>=chunkCount-1){ - onFinish(); - return true; - } - return false; - } - - private void addBuffer(FriendlyByteBuf input){ - final int size = input.readableBytes(); - final int cap = networkedBuf.capacity()-networkedBuf.writerIndex(); - - if (cap < size){ - networkedBuf.capacity(networkedBuf.writerIndex() + size); - } - input.readBytes(networkedBuf, size); - input.clear(); - } - - protected void onFinish(){ - incomingBuffer.clear(); - incomingBuffer = null; - - final BaseDataHandler baseHandler = descriptor.INSTANCE.get(); - if (baseHandler instanceof DataHandler.FromServer handler){ - handler.receiveFromMemory(networkedBuf); - } - } - - Map incomingBuffer = new HashMap<>(); - int lastReadSerial = -1; - int receivedCount = 0; - public void processReceived(FriendlyByteBuf buf, int serialNo, int size){ - receivedCount++; - - if (lastReadSerial == serialNo-1){ - addBuffer(buf); - lastReadSerial = serialNo; - } else { - //not sure if order is guaranteed by the underlying system! - boolean haveAll = true; - for (int nr = lastReadSerial+1; nr < serialNo-1; nr++){ - if (incomingBuffer.get(nr) == null){ - haveAll = false; - break; - } - } - - if (haveAll){ - for (int nr = lastReadSerial+1; nr < serialNo-1; nr++){ - addBuffer(incomingBuffer.get(nr)); - incomingBuffer.put(nr, null); - } - addBuffer(buf); - lastReadSerial = serialNo; - } else { - incomingBuffer.put(serialNo, buf); - } - } - } - } - - /** - * Responsible for splitting an outgoing ByteBuffer into several smaller Chunks and - * send them as seperate messages to the {@link Chunker}-Channel - */ - public static class PacketChunkSender { - private final FriendlyByteBuf networkedBuf; - public final UUID uuid; - public final int chunkCount; - public final int size; - public final ResourceLocation origin; - - public PacketChunkSender(FriendlyByteBuf buf, ResourceLocation origin){ - networkedBuf = buf; - - size = buf.readableBytes(); - chunkCount = (int)Math.ceil((double)size / MAX_PAYLOAD_SIZE); - uuid = UUID.randomUUID(); - this.origin = origin; - } - - public void sendChunks(Collection players){ - BCLib.LOGGER.info("Sending Request in " + chunkCount + " Packet-Chunks"); - for (int i=-1; i - * For Details refer to {@link HelloServer} - */ -public class HelloClient extends DataHandler.FromServer { - public record OfferedModInfo(String version, int size, boolean canDownload) { - } - public interface IServerModMap extends Map {} - public static class ServerModMap extends HashMap implements IServerModMap {} - - public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_client"), HelloClient::new, false, false); - - public HelloClient() { - super(DESCRIPTOR.IDENTIFIER); - } - - static String getBCLibVersion() { - return ModUtil.getModVersion(BCLib.MOD_ID); - } - - @Override - protected boolean prepareDataOnServer() { - if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the server."); - return false; - } - - AutoSync.loadSyncFolder(); - return true; - } - - @Override - protected void serializeDataOnServer(FriendlyByteBuf buf) { - final String vbclib = getBCLibVersion(); - BCLib.LOGGER.info("Sending Hello to Client. (server=" + vbclib + ")"); - - //write BCLibVersion (=protocol version) - buf.writeInt(ModUtil.convertModVersion(vbclib)); - - if (Configs.SERVER_CONFIG.isOfferingMods() || Configs.SERVER_CONFIG.isOfferingInfosForMods()) { - List mods = DataExchangeAPI.registeredMods(); - final List inmods = mods; - if (Configs.SERVER_CONFIG.isOfferingAllMods() || Configs.SERVER_CONFIG.isOfferingInfosForMods()){ - mods = new ArrayList<>(inmods.size()); - mods.addAll(inmods); - mods.addAll(ModUtil - .getMods() - .entrySet() - .stream() - .filter(entry -> entry.getValue().metadata.getEnvironment()!= ModEnvironment.SERVER && !inmods.contains(entry.getKey())) - .map(entry -> entry.getKey()) - .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()); - for (String modID : mods) { - final String ver = ModUtil.getModVersion(modID); - int size = 0; - - final ModInfo mi = ModUtil.getModInfo(modID); - if (mi != null) { - try { - size = (int) Files.size(mi.jarPath); - } catch (IOException e) { - BCLib.LOGGER.error("Unable to get File Size: " + e.getMessage()); - } - } - - - writeString(buf, modID); - buf.writeInt(ModUtil.convertModVersion(ver)); - buf.writeInt(size); - final boolean canDownload = size>0 && Configs.SERVER_CONFIG.isOfferingMods() && (Configs.SERVER_CONFIG.isOfferingAllMods() || inmods.contains(modID)); - buf.writeBoolean(canDownload); - - BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver + " (size: " + PathUtil.humanReadableFileSize(size) + ", download="+canDownload+")"); - } - } - else { - BCLib.LOGGER.info("Server will not list Mods."); - buf.writeInt(0); - } - - if (Configs.SERVER_CONFIG.isOfferingFiles() || Configs.SERVER_CONFIG.isOfferingConfigs()) { - //do only include files that exist on the server - final List existingAutoSyncFiles = AutoSync.getAutoSyncFiles() - .stream() - .filter(e -> e.fileName.exists()) - .filter(e -> (e.isConfigFile() && Configs.SERVER_CONFIG.isOfferingConfigs()) || (e instanceof AutoFileSyncEntry.ForDirectFileRequest && Configs.SERVER_CONFIG.isOfferingFiles())) - .collect(Collectors.toList()); - - //send config Data - buf.writeInt(existingAutoSyncFiles.size()); - for (AutoFileSyncEntry entry : existingAutoSyncFiles) { - entry.serialize(buf); - BCLib.LOGGER.info(" - Offering " + (entry.isConfigFile() ? "Config " : "File ") + entry); - } - } - else { - BCLib.LOGGER.info("Server will neither offer Files nor Configs."); - buf.writeInt(0); - } - - if (Configs.SERVER_CONFIG.isOfferingFiles()) { - buf.writeInt(AutoSync.syncFolderDescriptions.size()); - AutoSync.syncFolderDescriptions.forEach(desc -> { - BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete=" + desc.removeAdditionalFiles + ")"); - desc.serialize(buf); - }); - } - else { - BCLib.LOGGER.info("Server will not offer Sync Folders."); - buf.writeInt(0); - } - - buf.writeBoolean(Configs.SERVER_CONFIG.isOfferingInfosForMods()); - } - - String bclibVersion = "0.0.0"; - - - - IServerModMap modVersion = new ServerModMap(); - List autoSyncedFiles = null; - List autoSynFolders = null; - boolean serverPublishedModInfo = false; - - @Environment(EnvType.CLIENT) - @Override - protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { - //read BCLibVersion (=protocol version) - bclibVersion = ModUtil.convertModVersion(buf.readInt()); - - //read Plugin Versions - modVersion = new ServerModMap(); - int count = buf.readInt(); - for (int i = 0; i < count; i++) { - final String id = readString(buf); - final String version = ModUtil.convertModVersion(buf.readInt()); - final int size; - final boolean canDownload; - //since v0.4.1 we also send the size of the mod-File - size = buf.readInt(); - canDownload = buf.readBoolean(); - modVersion.put(id, new OfferedModInfo(version, size, canDownload)); - } - - //read config Data - count = buf.readInt(); - autoSyncedFiles = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - //System.out.println("Deserializing "); - AutoSync.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf); - autoSyncedFiles.add(t); - //System.out.println(t.first); - } - - - autoSynFolders = new ArrayList<>(1); - //since v0.4.1 we also send the sync folders - final int folderCount = buf.readInt(); - for (int i = 0; i < folderCount; i++) { - SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf); - autoSynFolders.add(desc); - } - - serverPublishedModInfo = buf.readBoolean(); - } - - @Environment(EnvType.CLIENT) - private void processAutoSyncFolder(final List filesToRequest, final List filesToRemove) { - if (!Configs.CLIENT_CONFIG.isAcceptingFiles()) { - return; - } - - if (autoSynFolders.size() > 0) { - BCLib.LOGGER.info("Folders offered by Server:"); - } - - autoSynFolders.forEach(desc -> { - //desc contains the fileCache sent from the server, load the local version to get hold of the actual file cache on the client - SyncFolderDescriptor localDescriptor = AutoSync.getSyncFolderDescriptor(desc.folderID); - if (localDescriptor != null) { - BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")"); - localDescriptor.invalidateCache(); - - desc.relativeFilesStream() - .filter(desc::discardChildElements) - .forEach(subFile -> { - BCLib.LOGGER.warning(" * " + subFile.relPath + " (REJECTED)"); - }); - - - if (desc.removeAdditionalFiles) { - List additionalFiles = localDescriptor.relativeFilesStream() - .filter(subFile -> !desc.hasRelativeFile(subFile)) - .map(desc::mapAbsolute) - .filter(desc::acceptChildElements) - .map(absPath -> new AutoSyncID.ForDirectFileRequest(desc.folderID, absPath.toFile())) - .collect(Collectors.toList()); - - additionalFiles.forEach(aid -> BCLib.LOGGER.info(" * " + desc.localFolder.relativize(aid.relFile.toPath()) + " (missing on server)")); - filesToRemove.addAll(additionalFiles); - } - - desc.relativeFilesStream() - .filter(desc::acceptChildElements) - .forEach(subFile -> { - SubFile localSubFile = localDescriptor.getLocalSubFile(subFile.relPath); - if (localSubFile != null) { - //the file exists locally, check if the hashes match - if (!localSubFile.hash.equals(subFile.hash)) { - BCLib.LOGGER.info(" * " + subFile.relPath + " (changed)"); - filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath))); - } - else { - BCLib.LOGGER.info(" * " + subFile.relPath); - } - } - else { - //the file is missing locally - BCLib.LOGGER.info(" * " + subFile.relPath + " (missing on client)"); - filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath))); - } - }); - - //free some memory - localDescriptor.invalidateCache(); - } - else { - BCLib.LOGGER.info(" - " + desc.folderID + " (Failed to find)"); - } - }); - } - - @Environment(EnvType.CLIENT) - private void processSingleFileSync(final List filesToRequest) { - final boolean debugHashes = Configs.CLIENT_CONFIG.shouldPrintDebugHashes(); - - if (autoSyncedFiles.size() > 0) { - BCLib.LOGGER.info("Files offered by Server:"); - } - - //Handle single sync files - //Single files need to be registered for sync on both client and server - //There are no restrictions to the target folder, but the client decides the final - //location. - for (AutoSync.AutoSyncTriple e : autoSyncedFiles) { - String actionString = ""; - FileContentWrapper contentWrapper = new FileContentWrapper(e.serverContent); - if (e.localMatch == null) { - actionString = "(unknown source -> omitting)"; - //filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID)); - } - else if (e.localMatch.needTransfer.test(e.localMatch.getFileHash(), e.serverHash, contentWrapper)) { - actionString = "(prepare update)"; - //we did not yet receive the new content - if (contentWrapper.getRawContent() == null) { - filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID)); - } - else { - filesToRequest.add(new AutoSyncID.WithContentOverride(e.serverHash.modID, e.serverHash.uniqueID, contentWrapper, e.localMatch.fileName)); - } - } - - BCLib.LOGGER.info(" - " + e + ": " + actionString); - if (debugHashes) { - BCLib.LOGGER.info(" * " + e.serverHash + " (Server)"); - BCLib.LOGGER.info(" * " + e.localMatch.getFileHash() + " (Client)"); - BCLib.LOGGER.info(" * local Content " + (contentWrapper.getRawContent() == null)); - } - } - } - - - @Environment(EnvType.CLIENT) - private void processModFileSync(final List filesToRequest, final Set mismatchingMods) { - for (Entry e : modVersion.entrySet()) { - final String localVersion = ModUtil.convertModVersion(ModUtil.convertModVersion(ModUtil.getModVersion(e.getKey()))); - final OfferedModInfo serverInfo = e.getValue(); - - ModInfo nfo = ModUtil.getModInfo(e.getKey()); - final boolean clientOnly = nfo!=null && nfo.metadata.getEnvironment()==ModEnvironment.CLIENT; - final boolean requestMod = !clientOnly && !serverInfo.version.equals(localVersion) && serverInfo.size > 0 && serverInfo.canDownload; - - BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.version + ", size=" + PathUtil.humanReadableFileSize(serverInfo.size) + (requestMod ? ", requesting" : "") + (serverInfo.canDownload ? "" :", not offered") + (clientOnly?", client only":"")+ ")"); - if (requestMod) { - filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverInfo.version)); - } - if (!serverInfo.version.equals(localVersion)) { - mismatchingMods.add(e.getKey()); - } - } - - mismatchingMods.addAll(ModListScreen.localMissing(modVersion)); - mismatchingMods.addAll(ModListScreen.serverMissing(modVersion)); - } - - @Override - protected boolean isBlocking() { - return true; - } - - @Environment(EnvType.CLIENT) - @Override - protected void runOnClientGameThread(Minecraft client) { - if (!Configs.CLIENT_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the client."); - return; - } - final String localBclibVersion = getBCLibVersion(); - BCLib.LOGGER.info("Received Hello from Server. (client=" + localBclibVersion + ", server=" + bclibVersion + ")"); - - if (ModUtil.convertModVersion(localBclibVersion) != ModUtil.convertModVersion(bclibVersion)){ - showBCLibError(client); - return; - } - - final List filesToRequest = new ArrayList<>(2); - final List filesToRemove = new ArrayList<>(2); - final Set mismatchingMods = new HashSet<>(2); - - - processModFileSync(filesToRequest, mismatchingMods); - processSingleFileSync(filesToRequest); - processAutoSyncFolder(filesToRequest, filesToRemove); - - //Handle folder sync - //Both client and server need to know about the folder you want to sync - //Files can only get placed within that folder - - if ((filesToRequest.size() > 0 || filesToRemove.size() > 0) && ( Configs.CLIENT_CONFIG.isAcceptingMods() || Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles())) { - showSyncFilesScreen(client, filesToRequest, filesToRemove); - return; - } else if (serverPublishedModInfo && mismatchingMods.size()>0 && Configs.CLIENT_CONFIG.isShowingModInfo()) { - client.setScreen(new ModListScreen(client.screen, Component.translatable("title.bclib.modmissmatch"), Component.translatable("message.bclib.modmissmatch"), CommonComponents.GUI_PROCEED, ModUtil.getMods(), modVersion)); - return; - } - } - - @Environment(EnvType.CLIENT) - protected void showBCLibError(Minecraft client) { - BCLib.LOGGER.error("BCLib differs on client and server."); - client.setScreen(new WarnBCLibVersionMismatch((download) -> { - if (download) { - requestBCLibDownload(); - - this.onCloseSyncFilesScreen(); - } else { - Minecraft.getInstance() - .setScreen(null); - } - })); - } - - @Environment(EnvType.CLIENT) - protected void showSyncFilesScreen(Minecraft client, List files, final List filesToRemove) { - int configFiles = 0; - int singleFiles = 0; - int folderFiles = 0; - int modFiles = 0; - - for (AutoSyncID aid : files) { - if (aid.isConfigFile()) { - configFiles++; - } - else if (aid instanceof AutoSyncID.ForModFileRequest) { - modFiles++; - } - else if (aid instanceof AutoSyncID.ForDirectFileRequest) { - folderFiles++; - } - else { - singleFiles++; - } - } - - client.setScreen(new SyncFilesScreen(modFiles, configFiles, singleFiles, folderFiles, filesToRemove.size(), modVersion, (downloadMods, downloadConfigs, downloadFiles, removeFiles) -> { - if (downloadMods || downloadConfigs || downloadFiles) { - BCLib.LOGGER.info("Updating local Files:"); - List localChanges = new ArrayList<>(files.toArray().length); - List requestFiles = new ArrayList<>(files.toArray().length); - - files.forEach(aid -> { - if (aid.isConfigFile() && downloadConfigs) { - processOfferedFile(requestFiles, aid); - } - else if (aid instanceof AutoSyncID.ForModFileRequest && downloadMods) { - processOfferedFile(requestFiles, aid); - } - else if (downloadFiles) { - processOfferedFile(requestFiles, aid); - } - }); - - requestFileDownloads(requestFiles); - } - if (removeFiles) { - filesToRemove.forEach(aid -> { - BCLib.LOGGER.info(" - " + aid.relFile + " (removing)"); - aid.relFile.delete(); - }); - } - - this.onCloseSyncFilesScreen(); - })); - } - - @Environment(EnvType.CLIENT) - private void onCloseSyncFilesScreen(){ - Minecraft.getInstance() - .setScreen(ChunkerProgress.getProgressScreen()); - } - - private void processOfferedFile(List requestFiles, AutoSyncID aid) { - if (aid instanceof WithContentOverride) { - final WithContentOverride aidc = (WithContentOverride) aid; - BCLib.LOGGER.info(" - " + aid + " (updating Content)"); - - SendFiles.writeSyncedFile(aid, aidc.contentWrapper.getRawContent(), aidc.localFile); - } - else { - requestFiles.add(aid); - BCLib.LOGGER.info(" - " + aid + " (requesting)"); - } - } - - private void requestBCLibDownload() { - BCLib.LOGGER.warning("Starting download of BCLib"); - requestFileDownloads(List.of(new AutoSyncID.ForModFileRequest(BCLib.MOD_ID, bclibVersion))); - } - - @Environment(EnvType.CLIENT) - private void requestFileDownloads(List files) { - BCLib.LOGGER.info("Starting download of Files:" + files.size()); - - final ProgressScreen progress = new ProgressScreen(null, Component.translatable("title.bclib.filesync.progress"), Component.translatable("message.bclib.filesync.progress")); - progress.progressStart(Component.translatable("message.bclib.filesync.progress.stage.empty")); - ChunkerProgress.setProgressScreen(progress); - - DataExchangeAPI.send(new RequestFiles(files)); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloServer.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloServer.java deleted file mode 100644 index 4852c5c2..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloServer.java +++ /dev/null @@ -1,109 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.entity.player.Player; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.DataHandlerDescriptor; -import ru.bclib.config.Configs; -import ru.bclib.util.ModUtil; - -import java.io.File; - -/** - * This message is sent once a player enters the world. It initiates a sequence of Messages that will sync files between both - * client and server. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Description
ServerClient
Player enters World
<--{@link HelloServer}Sends the current BLib-Version installed on the Client
{@link HelloClient}-->Sends the current BClIb-Version, the Version of all Plugins and data for all AutpoSync-Files - * ({@link DataExchangeAPI#addAutoSyncFile(String, File)} on the Server
<--{@link RequestFiles}Request missing or out of sync Files from the Server
{@link SendFiles}-->Send Files from the Server to the Client
- */ -public class HelloServer extends DataHandler.FromClient { - public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_server"), HelloServer::new, true, false); - - protected String bclibVersion = "0.0.0"; - - public HelloServer() { - super(DESCRIPTOR.IDENTIFIER); - } - - @Environment(EnvType.CLIENT) - @Override - protected boolean prepareDataOnClient() { - if (! Configs.CLIENT_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the client."); - return false; - } - - return true; - } - - @Environment(EnvType.CLIENT) - @Override - protected void serializeDataOnClient(FriendlyByteBuf buf) { - BCLib.LOGGER.info("Sending hello to server."); - buf.writeInt(ModUtil.convertModVersion(HelloClient.getBCLibVersion())); - } - - @Override - protected void deserializeIncomingDataOnServer(FriendlyByteBuf buf, Player player, PacketSender responseSender) { - bclibVersion = ModUtil.convertModVersion(buf.readInt()); - } - - @Override - protected void runOnServerGameThread(MinecraftServer server, Player player) { - if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the server."); - return; - } - - String localBclibVersion = HelloClient.getBCLibVersion(); - BCLib.LOGGER.info("Received Hello from Client. (server=" + localBclibVersion + ", client=" + bclibVersion + ")"); - - if (!server.isPublished()) { - BCLib.LOGGER.info("Auto-Sync is disabled for Singleplayer worlds."); - return; - } - - reply(new HelloClient(), server); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/RequestFiles.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/RequestFiles.java deleted file mode 100644 index 43b840fd..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/RequestFiles.java +++ /dev/null @@ -1,99 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.entity.player.Player; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.DataHandlerDescriptor; -import ru.bclib.config.Configs; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -public class RequestFiles extends DataHandler.FromClient { - public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "request_files"), RequestFiles::new, false, false); - static String currentToken = ""; - - protected List files; - - private RequestFiles() { - this(null); - } - - public RequestFiles(List files) { - super(DESCRIPTOR.IDENTIFIER); - this.files = files; - } - - @Environment(EnvType.CLIENT) - @Override - protected boolean prepareDataOnClient() { - if (! Configs.CLIENT_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the client."); - return false; - } - return true; - } - - @Environment(EnvType.CLIENT) - @Override - protected void serializeDataOnClient(FriendlyByteBuf buf) { - newToken(); - writeString(buf, currentToken); - - buf.writeInt(files.size()); - - for (AutoSyncID a : files) { - a.serializeData(buf); - } - } - - String receivedToken = ""; - - @Override - protected void deserializeIncomingDataOnServer(FriendlyByteBuf buf, Player player, PacketSender responseSender) { - receivedToken = readString(buf); - int size = buf.readInt(); - files = new ArrayList<>(size); - - BCLib.LOGGER.info("Client requested " + size + " Files:"); - for (int i = 0; i < size; i++) { - AutoSyncID asid = AutoSyncID.deserializeData(buf); - files.add(asid); - BCLib.LOGGER.info(" - " + asid); - } - - - } - - @Override - protected void runOnServerGameThread(MinecraftServer server, Player player) { - if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the server."); - return; - } - - List syncEntries = files.stream() - .map(asid -> AutoFileSyncEntry.findMatching(asid)) - .filter(e -> e != null) - .collect(Collectors.toList()); - - reply(new SendFiles(syncEntries, receivedToken), server); - } - - public static void newToken() { - currentToken = UUID.randomUUID() - .toString(); - } - - static { - newToken(); - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java deleted file mode 100644 index cd1e21de..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java +++ /dev/null @@ -1,215 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.minecraft.client.Minecraft; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.DataHandlerDescriptor; -import ru.bclib.config.Configs; -import ru.bclib.gui.screens.ConfirmRestartScreen; -import ru.bclib.util.Pair; -import ru.bclib.util.PathUtil; -import ru.bclib.util.Triple; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class SendFiles extends DataHandler.FromServer { - public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "send_files"), SendFiles::new, false, false); - - protected List files; - private String token; - - public SendFiles() { - this(null, ""); - } - - public SendFiles(List files, String token) { - super(DESCRIPTOR.IDENTIFIER); - this.files = files; - this.token = token; - } - - @Override - protected boolean prepareDataOnServer() { - if (!Configs.SERVER_CONFIG.isAllowingAutoSync()) { - BCLib.LOGGER.info("Auto-Sync was disabled on the server."); - return false; - } - - return true; - } - - @Override - protected void serializeDataOnServer(FriendlyByteBuf buf) { - List existingFiles = files.stream() - .filter(e -> e!=null &&e.fileName!=null && e.fileName.exists()) - .collect(Collectors.toList()); - /* - //this will try to send a file that was not registered or requested by the client - existingFiles.add(new AutoFileSyncEntry("none", new File("D:\\MinecraftPlugins\\BetterNether\\run\\server.properties"),true,(a, b, content) -> { - System.out.println("Got Content:" + content.length); - return true; - }));*/ - - /*//this will try to send a folder-file that was not registered or requested by the client - existingFiles.add(new AutoFileSyncEntry.ForDirectFileRequest(DataExchange.SYNC_FOLDER.folderID, new File("test.json"), DataExchange.SYNC_FOLDER.mapAbsolute("test.json").toFile()));*/ - - /*//this will try to send a folder-file that was not registered or requested by the client and is outside the base-folder - existingFiles.add(new AutoFileSyncEntry.ForDirectFileRequest(DataExchange.SYNC_FOLDER.folderID, new File("../breakout.json"), DataExchange.SYNC_FOLDER.mapAbsolute("../breakout.json").toFile()));*/ - - - writeString(buf, token); - buf.writeInt(existingFiles.size()); - - BCLib.LOGGER.info("Sending " + existingFiles.size() + " Files to Client:"); - for (AutoFileSyncEntry entry : existingFiles) { - int length = entry.serializeContent(buf); - BCLib.LOGGER.info(" - " + entry + " (" + PathUtil.humanReadableFileSize(length) + ")"); - } - } - - private List> receivedFiles; - - @Environment(EnvType.CLIENT) - @Override - protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { - if ( Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles() || Configs.CLIENT_CONFIG.isAcceptingMods()) { - token = readString(buf); - if (!token.equals(RequestFiles.currentToken)) { - RequestFiles.newToken(); - BCLib.LOGGER.error("Unrequested File Transfer!"); - receivedFiles = new ArrayList<>(0); - return; - } - RequestFiles.newToken(); - - int size = buf.readInt(); - receivedFiles = new ArrayList<>(size); - BCLib.LOGGER.info("Server sent " + size + " Files:"); - for (int i = 0; i < size; i++) { - Triple p = AutoFileSyncEntry.deserializeContent(buf); - if (p.first != null) { - final String type; - if (p.first.isConfigFile() && Configs.CLIENT_CONFIG.isAcceptingConfigs()) { - receivedFiles.add(p); - type = "Accepted Config "; - } else if (p.first instanceof AutoFileSyncEntry.ForModFileRequest && Configs.CLIENT_CONFIG.isAcceptingMods()){ - receivedFiles.add(p); - type = "Accepted Mod "; - } else if ( Configs.CLIENT_CONFIG.isAcceptingFiles()){ - receivedFiles.add(p); - type = "Accepted File "; - } else { - type = "Ignoring "; - } - BCLib.LOGGER.info(" - " + type + p.first + " (" + PathUtil.humanReadableFileSize(p.second.length) + ")"); - } - else { - BCLib.LOGGER.error(" - Failed to receive File " + p.third + ", possibly sent from a Mod that is not installed on the client."); - } - } - } - } - - @Environment(EnvType.CLIENT) - @Override - protected void runOnClientGameThread(Minecraft client) { - if ( Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles() || Configs.CLIENT_CONFIG.isAcceptingMods()) { - BCLib.LOGGER.info("Writing Files:"); - - for (Pair entry : receivedFiles) { - final AutoFileSyncEntry e = entry.first; - final byte[] data = entry.second; - - writeSyncedFile(e, data, e.fileName); - } - - showConfirmRestart(client); - } - } - - - @Environment(EnvType.CLIENT) - static void writeSyncedFile(AutoSyncID e, byte[] data, File fileName) { - if (fileName!=null && !PathUtil.isChildOf(PathUtil.GAME_FOLDER, fileName.toPath())){ - BCLib.LOGGER.error(fileName + " is not within game folder " + PathUtil.GAME_FOLDER); - return; - } - - if (!PathUtil.MOD_BAK_FOLDER.toFile().exists()){ - PathUtil.MOD_BAK_FOLDER.toFile().mkdirs(); - } - - Path path = fileName!=null?fileName.toPath():null; - Path removeAfter = null; - if (e instanceof AutoFileSyncEntry.ForModFileRequest mase){ - removeAfter = path; - int count = 0; - final String prefix = "_bclib_synced_"; - String name = prefix + mase.modID + "_" + mase.version.replace(".", "_") + ".jar"; - do { - if (path != null) { - //move to the same directory as the existing Mod - path = path.getParent() - .resolve(name); - } - else { - //move to the default mode location - path = PathUtil.MOD_FOLDER.resolve(name); - } - count++; - name = prefix + mase.modID + "_" + mase.version.replace(".", "_") + "__" + String.format("%03d", count) + ".jar"; - } while (path.toFile().exists()); - } - - BCLib.LOGGER.info(" - Writing " + path + " (" + PathUtil.humanReadableFileSize(data.length) + ")"); - try { - final File parentFile = path.getParent() - .toFile(); - if (!parentFile.exists()) { - parentFile.mkdirs(); - } - Files.write(path, data); - if (removeAfter != null){ - final String bakFileName = removeAfter.toFile().getName(); - String collisionFreeName = bakFileName; - Path targetPath; - int count = 0; - do { - targetPath = PathUtil.MOD_BAK_FOLDER.resolve(collisionFreeName); - count++; - collisionFreeName = String.format("%03d", count) + "_" + bakFileName; - } while (targetPath.toFile().exists()); - - BCLib.LOGGER.info(" - Moving " + removeAfter + " to " +targetPath); - removeAfter.toFile().renameTo(targetPath.toFile()); - } - AutoSync.didReceiveFile(e, fileName); - - - } - catch (IOException ioException) { - BCLib.LOGGER.error(" --> Writing " + fileName + " failed: " + ioException); - } - } - - @Environment(EnvType.CLIENT) - protected void showConfirmRestart(Minecraft client) { - client.setScreen(new ConfirmRestartScreen(() -> { - Minecraft.getInstance() - .setScreen(null); - client.stop(); - })); - - } -} diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SyncFolderDescriptor.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SyncFolderDescriptor.java deleted file mode 100644 index 71836a8c..00000000 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SyncFolderDescriptor.java +++ /dev/null @@ -1,206 +0,0 @@ -package ru.bclib.api.dataexchange.handler.autosync; - -import net.minecraft.network.FriendlyByteBuf; -import org.jetbrains.annotations.NotNull; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataHandler; -import ru.bclib.api.dataexchange.FileHash; -import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID.ForDirectFileRequest; -import ru.bclib.config.Configs; -import ru.bclib.util.PathUtil; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; - -public class SyncFolderDescriptor { - static class SubFile { - public final String relPath; - public final FileHash hash; - - - SubFile(String relPath, FileHash hash) { - this.relPath = relPath; - this.hash = hash; - } - - @Override - public String toString() { - return relPath; - } - - public void serialize(FriendlyByteBuf buf) { - DataHandler.writeString(buf, relPath); - hash.serialize(buf); - } - - public static SubFile deserialize(FriendlyByteBuf buf) { - final String relPath = DataHandler.readString(buf); - FileHash hash = FileHash.deserialize(buf); - return new SubFile(relPath, hash); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o instanceof String) return relPath.equals(o); - if (!(o instanceof SubFile)) return false; - SubFile subFile = (SubFile) o; - return relPath.equals(subFile.relPath); - } - - @Override - public int hashCode() { - return relPath.hashCode(); - } - } - - @NotNull - public final String folderID; - public final boolean removeAdditionalFiles; - @NotNull - public final Path localFolder; - - private List fileCache; - - public SyncFolderDescriptor(String folderID, Path localFolder, boolean removeAdditionalFiles) { - this.removeAdditionalFiles = removeAdditionalFiles; - this.folderID = folderID; - this.localFolder = localFolder; - fileCache = null; - } - - @Override - public String toString() { - return "SyncFolderDescriptor{" + "folderID='" + folderID + '\'' + ", removeAdditionalFiles=" + removeAdditionalFiles + ", localFolder=" + localFolder + ", files=" + (fileCache == null ? "?" : fileCache.size()) + "}"; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o instanceof String) { - return folderID.equals(o); - } - if (o instanceof ForDirectFileRequest) { - return folderID.equals(((ForDirectFileRequest) o).uniqueID); - } - if (!(o instanceof SyncFolderDescriptor)) return false; - SyncFolderDescriptor that = (SyncFolderDescriptor) o; - return folderID.equals(that.folderID); - } - - @Override - public int hashCode() { - return folderID.hashCode(); - } - - public int fileCount() { - return fileCache == null ? 0 : fileCache.size(); - } - - public void invalidateCache() { - fileCache = null; - } - - public void loadCache() { - if (fileCache == null) { - fileCache = new ArrayList<>(8); - PathUtil.fileWalker(localFolder.toFile(), p -> fileCache.add(new SubFile(localFolder.relativize(p) - .toString(), FileHash.create(p.toFile())))); - - /*//this tests if we can trick the system to load files that are not beneath the base-folder - if (!BCLib.isClient()) { - fileCache.add(new SubFile("../breakout.json", FileHash.create(mapAbsolute("../breakout.json").toFile()))); - }*/ - } - } - - public void serialize(FriendlyByteBuf buf) { - final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(AutoSync.SYNC_CATEGORY, "debugHashes", false); - loadCache(); - - DataHandler.writeString(buf, folderID); - buf.writeBoolean(removeAdditionalFiles); - buf.writeInt(fileCache.size()); - fileCache.forEach(fl -> { - BCLib.LOGGER.info(" - " + fl.relPath); - if (debugHashes) { - BCLib.LOGGER.info(" " + fl.hash); - } - fl.serialize(buf); - }); - } - - public static SyncFolderDescriptor deserialize(FriendlyByteBuf buf) { - final String folderID = DataHandler.readString(buf); - final boolean remAddFiles = buf.readBoolean(); - final int count = buf.readInt(); - SyncFolderDescriptor localDescriptor = AutoSync.getSyncFolderDescriptor(folderID); - - final SyncFolderDescriptor desc; - if (localDescriptor != null) { - desc = new SyncFolderDescriptor(folderID, localDescriptor.localFolder, localDescriptor.removeAdditionalFiles && remAddFiles); - desc.fileCache = new ArrayList<>(count); - } - else { - BCLib.LOGGER.warning(BCLib.isClient() ? "Client" : "Server" + " does not know Sync-Folder ID '" + folderID + "'"); - desc = null; - } - - for (int i = 0; i < count; i++) { - SubFile relPath = SubFile.deserialize(buf); - if (desc != null) desc.fileCache.add(relPath); - } - - return desc; - } - - //Note: make sure loadCache was called before using this - boolean hasRelativeFile(String relFile) { - return fileCache.stream() - .filter(sf -> sf.equals(relFile)) - .findFirst() - .isPresent(); - } - - //Note: make sure loadCache was called before using this - boolean hasRelativeFile(SubFile subFile) { - return hasRelativeFile(subFile.relPath); - } - - //Note: make sure loadCache was called before using this - SubFile getLocalSubFile(String relPath) { - return fileCache.stream() - .filter(sf -> sf.relPath.equals(relPath)) - .findFirst() - .orElse(null); - } - - Stream relativeFilesStream() { - loadCache(); - return fileCache.stream(); - } - - public Path mapAbsolute(String relPath) { - return this.localFolder.resolve(relPath) - .normalize(); - } - - public Path mapAbsolute(SubFile subFile) { - return this.localFolder.resolve(subFile.relPath) - .normalize(); - } - - public boolean acceptChildElements(Path absPath) { - return PathUtil.isChildOf(this.localFolder, absPath); - } - - public boolean acceptChildElements(SubFile subFile) { - return acceptChildElements(mapAbsolute(subFile)); - } - - public boolean discardChildElements(SubFile subFile) { - return !acceptChildElements(subFile); - } -} diff --git a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java deleted file mode 100644 index 9a39bae7..00000000 --- a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java +++ /dev/null @@ -1,628 +0,0 @@ -package ru.bclib.api.datafixer; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.Util; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.components.toasts.SystemToast; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.worldselection.EditWorldScreen; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.Tag; -import net.minecraft.network.chat.Component; - -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.storage.RegionFile; -import net.minecraft.world.level.storage.LevelResource; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; -import org.jetbrains.annotations.NotNull; -import ru.bclib.BCLib; -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; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -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 - */ -public class DataFixerAPI { - static final Logger LOGGER = new Logger("DataFixerAPI"); - static class State { - public boolean didFail = false; - protected ArrayList 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 - public static interface Callback { - public void call(); - } - - private static boolean wrapCall(LevelStorageSource levelSource, String levelID, Function runWithLevel) { - LevelStorageSource.LevelStorageAccess levelStorageAccess; - try { - levelStorageAccess = levelSource.createAccess(levelID); - } - catch (IOException e) { - BCLib.LOGGER.warning("Failed to read level {} data", levelID, e); - SystemToast.onWorldAccessFailure(Minecraft.getInstance(), levelID); - Minecraft.getInstance().setScreen(null); - return true; - } - - boolean returnValue = runWithLevel.apply(levelStorageAccess); - - try { - levelStorageAccess.close(); - } - catch (IOException e) { - BCLib.LOGGER.warning("Failed to unlock access to level {}", levelID, e); - } - - return returnValue; - } - - /** - * Will apply necessary Patches to the world. - * - * @param levelSource The SourceStorage for this Minecraft instance, You can get this using - * {@code Minecraft.getInstance().getLevelSource()} - * @param levelID The ID of the Level you want to patch - * @param showUI {@code true}, if you want to present the user with a Screen that offers to backup the world - * before applying the patches - * @param onResume When this method retursn {@code true}, this function will be called when the world is ready - * @return {@code true} if the UI was displayed. The UI is only displayed if {@code showUI} was {@code true} and - * patches were enabled in the config and the Guardian did find any patches that need to be applied to the world. - * - */ - public static boolean fixData(LevelStorageSource levelSource, String levelID, boolean showUI, Consumer onResume) { - return wrapCall(levelSource, levelID, (levelStorageAccess) -> fixData(levelStorageAccess, showUI, onResume)); - } - - /** - * Will apply necessary Patches to the world. - * - * @param levelStorageAccess The access class of the level you want to patch - * @param showUI {@code true}, if you want to present the user with a Screen that offers to backup the world - * before applying the patches - * @param onResume When this method retursn {@code true}, this function will be called when the world is ready - * @return {@code true} if the UI was displayed. The UI is only displayed if {@code showUI} was {@code true} and - * patches were enabled in the config and the Guardian did find any patches that need to be applied to the world. - * - */ - public static boolean fixData(LevelStorageSource.LevelStorageAccess levelStorageAccess, boolean showUI, Consumer onResume){ - File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile(); - File levelDat = levelStorageAccess.getLevelPath(LevelResource.LEVEL_DATA_FILE).toFile(); - boolean newWorld = false; - if (!levelDat.exists()) { - BCLib.LOGGER.info("Creating a new World, no fixes needed"); - newWorld = true; - } - - initializeWorldData(levelPath, newWorld); - if (newWorld) return false; - - return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume); - } - /** - * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the - * current versions of the plugins. - *

- * This implementation will create a new {@link LevelStorageAccess} and call {@link #initializeWorldData(File, boolean)} - * using the provided root path. - * @param levelSource The SourceStorage for this Minecraft instance, You can get this using - * {@code Minecraft.getInstance().getLevelSource()} - * @param levelID The ID of the Level you want to patch - * @param newWorld {@code true} if this is a fresh world - */ - public static void initializeWorldData(LevelStorageSource levelSource, String levelID, boolean newWorld) { - wrapCall(levelSource, levelID, (levelStorageAccess) -> { - initializeWorldData(levelStorageAccess, newWorld); - return true; - }); - } - - /** - * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the - * current versions of the plugins. - * @param access levelAccess for the worldd - * @param newWorld {@code true} if this is a fresh world - * - */ - public static void initializeWorldData(LevelStorageAccess access, boolean newWorld){ - initializeWorldData(access.getLevelPath(LevelResource.ROOT).toFile(), newWorld); - } - - /** - * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the - * current versions of the plugins. - * @param levelBaseDir Folder of the world - * @param newWorld {@code true} if this is a fresh world - * - */ - public static void initializeWorldData(File levelBaseDir, boolean newWorld){ - WorldDataAPI.load(new File(levelBaseDir, "data")); - - if (newWorld){ - getMigrationProfile().markApplied(); - WorldDataAPI.saveFile(BCLib.MOD_ID); - } - } - - @Environment(EnvType.CLIENT) - private static AtomicProgressListener showProgressScreen(){ - ProgressScreen ps = new ProgressScreen(Minecraft.getInstance().screen, Component.translatable("title.bclib.datafixer.progress"), Component.translatable("message.bclib.datafixer.progress")); - Minecraft.getInstance().setScreen(ps); - return ps; - } - - private static boolean fixData(File dir, String levelID, boolean showUI, Consumer onResume) { - MigrationProfile profile = loadProfileIfNeeded(dir); - - BiConsumer runFixes = (createBackup, applyFixes) -> { - final AtomicProgressListener progress; - if (applyFixes) { - if (showUI) { - progress = showProgressScreen(); - } else { - progress = new AtomicProgressListener() { - private long timeStamp = Util.getMillis(); - private AtomicInteger counter = new AtomicInteger(0); - - @Override - public void incAtomic(int maxProgress) { - int percentage = (100 * counter.incrementAndGet()) / maxProgress; - if (Util.getMillis() - this.timeStamp >= 1000L) { - this.timeStamp = Util.getMillis(); - BCLib.LOGGER.info((String) "Patching... {}%", percentage); - } - } - - @Override - public void resetAtomic() { - counter = new AtomicInteger(0); - } - - public void stop() { - } - - public void progressStage(Component component) { - BCLib.LOGGER.info((String) "Patcher Stage... {}%", component.getString()); - } - }; - } - } else { - progress = null; - } - - Supplier runner = () -> { - if (createBackup) { - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.waitbackup")); - EditWorldScreen.makeBackupAndShowToast(Minecraft.getInstance().getLevelSource(), levelID); - } - - if (applyFixes) { - return runDataFixes(dir, profile, progress); - } - - return new State(); - }; - - if (showUI) { - Thread fixerThread = new Thread(() -> { - 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 { - State state = runner.get(); - if (state.hasError()){ - LOGGER.error("There were Errors while fixing the Level:"); - LOGGER.error(state.getErrorMessage()); - } - } - }; - - //we have some migrations - if (profile != null) { - //display the confirm UI. - if (showUI){ - showBackupWarning(levelID, runFixes); - return true; - } else { - BCLib.LOGGER.warning("Applying Fixes on Level"); - runFixes.accept(false, true); - } - } - 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.applyPatches()) { - LOGGER.info("World Patches are disabled"); - return null; - } - - MigrationProfile profile = getMigrationProfile(); - profile.runPrePatches(levelBaseDir); - - if (!profile.hasAnyFixes()) { - LOGGER.info("Everything up to date"); - return null; - } - - return profile; - } - - @NotNull - private static MigrationProfile getMigrationProfile() { - final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); - MigrationProfile profile = Patch.createMigrationData(patchConfig); - return profile; - } - - @Environment(EnvType.CLIENT) - static void showBackupWarning(String levelID, BiConsumer whenFinished){ - Minecraft.getInstance().setScreen(new ConfirmFixScreen((Screen) null, whenFinished::accept)); - } - - private static State runDataFixes(File dir, MigrationProfile profile, AtomicProgressListener progress) { - State state = new State(); - progress.resetAtomic(); - - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.reading")); - List players = getAllPlayers(dir); - List regions = getAllRegions(dir, null); - final int maxProgress = players.size()+regions.size()+4; - progress.incAtomic(maxProgress); - - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.players")); - players.parallelStream().forEach((file) -> { - fixPlayer(profile, state, file); - progress.incAtomic(maxProgress); - }); - - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.level")); - fixLevel(profile, state, dir); - progress.incAtomic(maxProgress); - - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.worlddata")); - try { - profile.patchWorldData(); - } catch (PatchDidiFailException e){ - state.didFail = true; - state.addError("Failed fixing worldconfig (" + e.getMessage() + ")"); - BCLib.LOGGER.error(e.getMessage()); - } - progress.incAtomic(maxProgress); - - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.regions")); - regions.parallelStream().forEach((file) -> { - fixRegion(profile, state, file); - progress.incAtomic(maxProgress); - }); - - if (!state.didFail) { - progress.progressStage(Component.translatable("message.bclib.datafixer.progress.saving")); - profile.markApplied(); - WorldDataAPI.saveFile(BCLib.MOD_ID); - } - progress.incAtomic(maxProgress); - - progress.stop(); - - return state; - } - - private static void fixLevel(MigrationProfile profile, State state, File levelBaseDir) { - try { - LOGGER.info("Inspecting level.dat in " + levelBaseDir); - - //load the level (could already contain patches applied by patchLevelDat) - CompoundTag level = profile.getLevelDat(levelBaseDir); - boolean[] changed = { profile.isLevelDatChanged() }; - - if (profile.getPrePatchException()!=null){ - throw profile.getPrePatchException(); - } - - if (level.contains("Data")) { - CompoundTag dataTag = (CompoundTag)level.get("Data"); - if (dataTag.contains("Player")) { - CompoundTag player = (CompoundTag)dataTag.get("Player"); - fixPlayerNbt(player, changed, profile); - } - } - - if (changed[0]) { - LOGGER.warning("Writing '{}'", profile.getLevelDatFile()); - NbtIo.writeCompressed(level, profile.getLevelDatFile()); - } - } - 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 = readNbt(file); - boolean[] changed = { false }; - fixPlayerNbt(player, changed, data); - - if (changed[0]) { - LOGGER.warning("Writing '{}'", file); - NbtIo.writeCompressed(player, file); - } - } - 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); - fixItemArrayWithID(inventory, changed, data, true); - - //Checking EnderChest - ListTag enderitems = player.getList("EnderItems", Tag.TAG_COMPOUND); - fixItemArrayWithID(enderitems, changed, data, true); - - //Checking ReceipBook - if (player.contains("recipeBook")) { - CompoundTag recipeBook = player.getCompound("recipeBook"); - changed[0] |= fixStringIDList(recipeBook, "recipes", data); - changed[0] |= fixStringIDList(recipeBook, "toBeDisplayed", data); - } - } - - static boolean fixStringIDList(CompoundTag root, String name, MigrationProfile data) { - boolean _changed = false; - if (root.contains(name)) { - ListTag items = root.getList(name, Tag.TAG_STRING); - ListTag newItems = new ListTag(); - - for (Tag tag : items) { - final StringTag str = (StringTag)tag; - final String replace = data.replaceStringFromIDs(str.getAsString()); - if (replace!=null) { - _changed = true; - newItems.add(StringTag.valueOf(replace)); - } else { - newItems.add(tag); - } - } - if (_changed) { - root.put(name, newItems); - } - } - return _changed; - } - - private static void fixRegion(MigrationProfile data, State state, File file) { - try { - Path path = file.toPath(); - LOGGER.info("Inspecting " + path); - boolean[] changed = new boolean[1]; - RegionFile region = new RegionFile(path, path.getParent(), true); - - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - ChunkPos pos = new ChunkPos(x, z); - changed[0] = false; - 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"))) { - // NbtIo.write(root, new File(file.toString() + "-" + x + "-" + z + ".nbt")); - // } - input.close(); - - //Checking TileEntities - ListTag tileEntities = root.getCompound("Level") - .getList("TileEntities", Tag.TAG_COMPOUND); - fixItemArrayWithID(tileEntities, changed, data, true); - - //Checking Entities - ListTag entities = root.getList("Entities", Tag.TAG_COMPOUND); - fixItemArrayWithID(entities, changed, data, true); - - //Checking Block Palette - ListTag sections = root.getCompound("Level") - .getList("Sections", Tag.TAG_COMPOUND); - sections.forEach((tag) -> { - ListTag palette = ((CompoundTag) tag).getList("Palette", Tag.TAG_COMPOUND); - palette.forEach((blockTag) -> { - 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]) { - LOGGER.warning("Writing '{}': {}/{}", file, x, z); - // NbtIo.write(root, new File(file.toString() + "-" + x + "-" + z + "-changed.nbt")); - DataOutputStream output = region.getChunkDataOutputStream(pos); - NbtIo.write(root, output); - output.close(); - } - } - } - } - region.close(); - } - catch (Exception e) { - 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) { - patchConfTag = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); - } - return patchConfTag; - } - - static void fixItemArrayWithID(ListTag items, boolean[] changed, MigrationProfile data, boolean recursive) { - items.forEach(inTag -> { - fixID((CompoundTag) inTag, changed, data, recursive); - }); - } - - - static void fixID(CompoundTag inTag, boolean[] changed, MigrationProfile data, boolean recursive) { - final CompoundTag tag = inTag; - - changed[0] |= data.replaceStringFromIDs(tag, "id"); - if (tag.contains("Item")) { - CompoundTag item = (CompoundTag)tag.get("Item"); - fixID(item, changed, data, recursive); - } - - if (recursive && tag.contains("Items")) { - fixItemArrayWithID(tag.getList("Items", Tag.TAG_COMPOUND), changed, data, true); - } - if (recursive && tag.contains("Inventory")) { - ListTag inventory = tag.getList("Inventory", Tag.TAG_COMPOUND); - fixItemArrayWithID(inventory, changed, data, true); - } - if (tag.contains("tag")) { - CompoundTag entityTag = (CompoundTag)tag.get("tag"); - if (entityTag.contains("BlockEntityTag")){ - CompoundTag blockEntityTag = (CompoundTag)entityTag.get("BlockEntityTag"); - fixID(blockEntityTag, changed, data, recursive); - /*ListTag items = blockEntityTag.getList("Items", Tag.TAG_COMPOUND); - fixItemArrayWithID(items, changed, data, recursive);*/ - } - } - } - - private static List getAllPlayers(File dir) { - List list = new ArrayList<>(); - dir = new File(dir, "playerdata"); - if (!dir.exists() || !dir.isDirectory()) { - return list; - } - for (File file : dir.listFiles()) { - if (file.isFile() && file.getName().endsWith(".dat")) { - list.add(file); - } - } - return list; - } - - private static List getAllRegions(File dir, List list) { - if (list == null) { - list = new ArrayList<>(); - } - for (File file : dir.listFiles()) { - if (file.isDirectory()) { - getAllRegions(file, list); - } else if (file.isFile() && file.getName().endsWith(".mca")) { - list.add(file); - } - } - return list; - } - - /** - * register a new Patch - * - * @param patch A #Supplier that will instantiate the new Patch Object - */ - public static void registerPatch(Supplier patch) { - Patch.getALL().add(patch.get()); - } - - private static CompoundTag readNbt(File file) throws IOException { - try { - return NbtIo.readCompressed(file); - } catch (ZipException | EOFException e){ - return NbtIo.read(file); - } - } - -} diff --git a/src/main/java/ru/bclib/api/datafixer/ForcedLevelPatch.java b/src/main/java/ru/bclib/api/datafixer/ForcedLevelPatch.java deleted file mode 100644 index 333d473b..00000000 --- a/src/main/java/ru/bclib/api/datafixer/ForcedLevelPatch.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.bclib.api.datafixer; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import org.jetbrains.annotations.NotNull; -import ru.bclib.interfaces.PatchBiFunction; -import ru.bclib.interfaces.PatchFunction; - -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 getIDReplacements() { - return new HashMap(); - } - - @Override - public final PatchFunction getWorldDataPatcher() { return null; } - - @Override - public final PatchBiFunction getBlockStatePatcher() { return null; } - - @Override - public final List getWorldDataIDPaths() { - return null; - } - - @Override - public PatchFunction 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); -} - diff --git a/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java b/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java deleted file mode 100644 index e0065845..00000000 --- a/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java +++ /dev/null @@ -1,375 +0,0 @@ -package ru.bclib.api.datafixer; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.Tag; -import org.jetbrains.annotations.NotNull; -import ru.bclib.BCLib; -import ru.bclib.api.WorldDataAPI; -import ru.bclib.interfaces.PatchBiFunction; -import ru.bclib.interfaces.PatchFunction; -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; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -public class MigrationProfile { - final Set mods; - final Map idReplacements; - final List> levelPatchers; - final List> statePatchers; - final List worldDataPatchers; - final Map> worldDataIDPaths; - - private final CompoundTag config; - private CompoundTag level; - private File levelBaseDir; - private boolean prePatchChangedLevelDat; - private boolean didRunPrePatch; - private Exception prePatchException; - - MigrationProfile(CompoundTag config, boolean applyAll) { - this.config = config; - - this.mods = Collections.unmodifiableSet(Patch.getALL() - .stream() - .map(p -> p.modID) - .collect(Collectors.toSet())); - - HashMap replacements = new HashMap(); - List> levelPatches = new LinkedList<>(); - List worldDataPatches = new LinkedList<>(); - List> statePatches = new LinkedList<>(); - HashMap> worldDataIDPaths = new HashMap<>(); - for (String modID : mods) { - - Patch.getALL() - .stream() - .filter(p -> p.modID.equals(modID)) - .forEach(patch -> { - List paths = patch.getWorldDataIDPaths(); - if (paths!=null) worldDataIDPaths.put(modID, paths); - - 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 { - DataFixerAPI.LOGGER.info("Ignoring " + patch); - } - }); - } - - this.worldDataIDPaths = Collections.unmodifiableMap(worldDataIDPaths); - 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 nbts = getAllNbts(dir, null); - nbts.parallelStream().forEach((file) -> { - DataFixerAPI.LOGGER.info("Loading NBT " + file); - try { - CompoundTag root = NbtIo.readCompressed(file); - boolean[] changed = {false}; - int spawnerIdx = -1; - if (root.contains("palette")){ - ListTag items = root.getList("palette", Tag.TAG_COMPOUND); - for (int idx=0; idx=0 && root.contains("blocks")){ - ListTag items = root.getList("blocks", Tag.TAG_COMPOUND); - for (int idx=0; idx getAllNbts(File dir, List 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){ - if (level == null || this.levelBaseDir==null || !this.levelBaseDir.equals(levelBaseDir)){ - runPrePatches(levelBaseDir); - } - return level; - } - - final public boolean isLevelDatChanged(){ - return prePatchChangedLevelDat; - } - - final public File getLevelDatFile(){ - return new File(levelBaseDir, "level.dat"); - } - - final public Exception getPrePatchException(){ - return prePatchException; - } - - - final public void runPrePatches(File levelBaseDir){ - if (didRunPrePatch){ - BCLib.LOGGER.warning("Already did run PrePatches for " + this.levelBaseDir + "."); - } - BCLib.LOGGER.info("Running Pre Patchers on " + levelBaseDir); - - this.levelBaseDir = levelBaseDir; - this.level = null; - this.prePatchException = null; - didRunPrePatch = true; - - this.prePatchChangedLevelDat = runPreLevelPatches(getLevelDatFile()); - } - - private boolean runPreLevelPatches(File levelDat){ - try { - level = NbtIo.readCompressed(levelDat); - - boolean changed = patchLevelDat(level); - return changed; - } - catch (IOException | PatchDidiFailException e) { - prePatchException = e; - return false; - } - } - - 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))); - if (config!=null) - config.putString(modID, Patch.maxPatchVersion(modID)); - } - } - - public String currentPatchVersion(@NotNull String modID) { - if (config==null || !config.contains(modID)) return "0.0.0"; - return config.getString(modID); - } - - public int currentPatchLevel(@NotNull String modID) { - return ModUtil.convertModVersion(currentPatchVersion(modID)); - } - - public boolean hasAnyFixes() { - boolean hasLevelDatPatches; - if (didRunPrePatch != false) { - hasLevelDatPatches = prePatchChangedLevelDat; - } else { - hasLevelDatPatches = levelPatchers.size()>0; - } - - return idReplacements.size() > 0 || hasLevelDatPatches || worldDataPatchers.size() > 0; - } - - public String replaceStringFromIDs(@NotNull String val) { - final String replace = idReplacements.get(val); - return replace; - } - - public boolean replaceStringFromIDs(@NotNull CompoundTag tag, @NotNull String key) { - if (!tag.contains(key)) return false; - - final String val = tag.getString(key); - final String replace = idReplacements.get(val); - - if (replace != null) { - DataFixerAPI.LOGGER.warning("Replacing ID '{}' with '{}'.", val, replace); - tag.putString(key, replace); - return true; - } - - return false; - } - - private boolean replaceIDatPath(@NotNull ListTag list, @NotNull String[] parts, int level){ - boolean[] changed = {false}; - if (level == parts.length-1) { - DataFixerAPI.fixItemArrayWithID(list, changed, this, true); - } else { - list.forEach(inTag -> changed[0] |= replaceIDatPath((CompoundTag)inTag, parts, level+1)); - } - return changed[0]; - } - - private boolean replaceIDatPath(@NotNull CompoundTag tag, @NotNull String[] parts, int level){ - boolean changed = false; - for (int i=level; i0) { - final String key = parts[parts.length-1]; - final byte type = tag.getTagType(key); - if (type == Tag.TAG_LIST) { - final ListTag list = tag.getList(key, Tag.TAG_COMPOUND); - final boolean[] _changed = {false}; - if (list.size()==0) { - _changed[0] = DataFixerAPI.fixStringIDList(tag, key, this); - } else { - DataFixerAPI.fixItemArrayWithID(list, _changed, this, true); - } - return _changed[0]; - } else if (type == Tag.TAG_STRING) { - return replaceStringFromIDs(tag, key); - } else if (type == Tag.TAG_COMPOUND) { - final CompoundTag cTag = tag.getCompound(key); - boolean[] _changed = {false}; - DataFixerAPI.fixID(cTag, _changed, this, true); - return _changed[0]; - } - } - - - return false; - } - - public boolean replaceIDatPath(@NotNull CompoundTag root, @NotNull String path){ - String[] parts = path.split("\\."); - return replaceIDatPath(root, parts, 0); - } - - public boolean patchLevelDat(@NotNull CompoundTag level) throws PatchDidiFailException { - boolean changed = false; - for (PatchFunction f : levelPatchers) { - changed |= f.apply(level, this); - } - return changed; - } - - public void patchWorldData() throws PatchDidiFailException { - for (Patch patch : worldDataPatchers) { - CompoundTag root = WorldDataAPI.getRootTag(patch.modID); - boolean changed = patch.getWorldDataPatcher().apply(root, this); - if (changed) { - WorldDataAPI.saveFile(patch.modID); - } - } - - for (Map.Entry> entry : worldDataIDPaths.entrySet()){ - CompoundTag root = WorldDataAPI.getRootTag(entry.getKey()); - boolean[] changed = {false}; - entry.getValue().forEach(path -> { - changed[0] |= replaceIDatPath(root, path); - }); - - if (changed[0]){ - WorldDataAPI.saveFile(entry.getKey()); - } - } - } - - public boolean patchBlockState(ListTag palette, ListTag states) throws PatchDidiFailException{ - boolean changed = false; - for (PatchBiFunction f : statePatchers) { - changed |= f.apply(palette, states, this); - } - return changed; - } -} diff --git a/src/main/java/ru/bclib/api/datafixer/Patch.java b/src/main/java/ru/bclib/api/datafixer/Patch.java deleted file mode 100644 index 248b406a..00000000 --- a/src/main/java/ru/bclib/api/datafixer/Patch.java +++ /dev/null @@ -1,228 +0,0 @@ -package ru.bclib.api.datafixer; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import org.jetbrains.annotations.NotNull; -import ru.bclib.interfaces.PatchBiFunction; -import ru.bclib.interfaces.PatchFunction; -import ru.bclib.util.ModUtil; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public abstract class Patch { - private static final List ALL = new ArrayList<>(10); - - /** - * The Patch-Level derived from {@link #version} - */ - public final int level; - - /** - * The Patch-Version string - */ - public final String version; - - /** - * The Mod-ID that registered this Patch - */ - - @NotNull - public final String modID; - - /** - * This Mod is tested for each level start - */ - public final boolean alwaysApply; - - static List getALL() { - return ALL; - } - - /** - * Returns the highest Patch-Version that is available for the given mod. If no patches were - * registerd for the mod, this will return 0.0.0 - * - * @param modID The ID of the mod you want to query - * @return The highest Patch-Version that was found - */ - public static String maxPatchVersion(@NotNull String modID) { - return ALL.stream().filter(p -> p.modID.equals(modID)).map(p -> p.version).reduce((p, c) -> c).orElse("0.0.0"); - } - - /** - * Returns the highest patch-level that is available for the given mod. If no patches were - * registerd for the mod, this will return 0 - * - * @param modID The ID of the mod you want to query - * @return The highest Patch-Level that was found - */ - public static int maxPatchLevel(@NotNull String modID) { - return ALL.stream().filter(p -> p.modID.equals(modID)).mapToInt(p -> p.level).max().orElse(0); - } - - /** - * Called by inheriting classes. - *

- * Performs some sanity checks on the values and might throw a #RuntimeException if any - * inconsistencies are found. - * - * @param modID The ID of the Mod you want to register a patch for. This should be your - * ModID only. The ModID can not be {@code null} or an empty String. - * @param version The mod-version that introduces the patch. This needs Semantic-Version String - * like x.x.x. Developers are responsible for registering their patches in the correct - * order (with increasing versions). You are not allowed to register a new - * Patch with a version lower or equal than - * {@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 || modID.isEmpty()) { - throw new RuntimeException("[INTERNAL ERROR] Patches need a valid modID!"); - } - - if (version == null || version.isEmpty()) { - throw new RuntimeException("Invalid Mod-Version"); - } - - this.version = version; - this.alwaysApply = alwaysApply; - this.level = ModUtil.convertModVersion(version); - if (!ALL.stream().filter(p -> p.modID.equals(modID)).noneMatch(p -> p.level >= this.level) || this.level <= 0) { - throw new RuntimeException("[INTERNAL ERROR] Patch-levels need to be created in ascending order beginning with 1."); - } - - this.modID = modID; - } - - @Override - public String toString() { - return "Patch{" + modID + ':' +version + ':' + level + '}'; - } - - - /** - * Return block data fixes. Fixes will be applied on world load if current patch-level for - * the linked mod is lower than the {@link #level}. - *

- * The default implementation of this method returns an empty map. - * - * @return The returned Map should contain the replacements. All occurences of the - * {@code KeySet} are replaced with the associated value. - */ - public Map getIDReplacements() { - return new HashMap(); - } - - /** - * Return a {@link PatchFunction} that is called with the content of level.dat. - *

- * 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 PatchFunction getLevelDatPatcher() { return null; } - - /** - * Return a {@link PatchFunction} that is called with the content from the - * {@link ru.bclib.api.WorldDataAPI} for this Mod. - * 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 PatchFunction 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 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. - *

- * 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* - * @return a new {@link MigrationProfile} Object. - */ - static MigrationProfile createMigrationData(CompoundTag 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 stores IDs in your {@link ru.bclib.api.WorldDataAPI}-File. - *

- * {@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()} - *

- * The end of the path can either be a {@link net.minecraft.nbt.StringTag}, a {@link net.minecraft.nbt.ListTag} or - * a {@link CompoundTag}. If the Path contains a non-leaf {@link net.minecraft.nbt.ListTag}, all members of that - * list will be processed. For example: - *

-	 *	 - global +
-	 *			  | - key (String)
-	 *			  | - items (List) +
-	 *							   | - { id (String) }
-	 *							   | - { id (String) }
-	 * 
- * The path global.items.id will fix all id-entries in the items-list, while the path - * global.key will only fix the key-entry. - *

- * if the leaf-entry (= the last part of the path, which would be items in global.items) is a - * {@link CompoundTag}, the system will fix any id entry. If the {@link CompoundTag} contains an item - * or tag.BlockEntityTag entry, the system will recursivley continue with those. If an items - * or inventory-{@link net.minecraft.nbt.ListTag} was found, the system will continue recursivley with - * every item of that list. - *

- * if the leaf-entry is a {@link net.minecraft.nbt.ListTag}, it is handle the same as a child items entry - * of a {@link CompoundTag}. - * - * @return {@code null} if nothing changes or a list of Paths in your {@link ru.bclib.api.WorldDataAPI}-File. - * Paths are dot-seperated (see {@link ru.bclib.api.WorldDataAPI#getCompoundTag(String, String)}). - */ - public List getWorldDataIDPaths() { - return null; - } -} diff --git a/src/main/java/ru/bclib/api/datafixer/PatchDidiFailException.java b/src/main/java/ru/bclib/api/datafixer/PatchDidiFailException.java deleted file mode 100644 index 737ba402..00000000 --- a/src/main/java/ru/bclib/api/datafixer/PatchDidiFailException.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.bclib.api.datafixer; - -public class PatchDidiFailException extends Exception { - public PatchDidiFailException(){ - super(); - } - public PatchDidiFailException(Exception e){ - super(e); - } -} diff --git a/src/main/java/ru/bclib/api/features/BCLCommonFeatures.java b/src/main/java/ru/bclib/api/features/BCLCommonFeatures.java deleted file mode 100644 index 51103503..00000000 --- a/src/main/java/ru/bclib/api/features/BCLCommonFeatures.java +++ /dev/null @@ -1,116 +0,0 @@ -package ru.bclib.api.features; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.levelgen.GenerationStep.Decoration; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; -import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration; -import net.minecraft.world.level.levelgen.placement.PlacementModifier; -import net.minecraft.world.level.levelgen.structure.templatesystem.BlockMatchTest; -import ru.bclib.world.features.BCLFeature; - -public class BCLCommonFeatures { - /** - * Will create a basic plant feature. - * @param id {@link ResourceLocation} feature ID. - * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. - * @param density iterations per chunk. - * @return new BCLFeature instance. - */ - public static BCLFeature makeVegetationFeature(ResourceLocation id, Feature feature, int density) { - return makeVegetationFeature(id, feature, density, false); - } - - /** - * Will create a basic plant feature. - * @param id {@link ResourceLocation} feature ID. - * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. - * @param density iterations per chunk. - * @param allHeight if {@code true} will generate plant on all layers, if {@code false} - only on surface. - * @return new BCLFeature instance. - */ - public static BCLFeature makeVegetationFeature(ResourceLocation id, Feature feature, int density, boolean allHeight) { - if (allHeight) { - return BCLFeatureBuilder.start(id, feature).countLayers(density).squarePlacement().onlyInBiome().build(); - } - else { - return BCLFeatureBuilder - .start(id, feature) - .countMax(density) - .squarePlacement() - .heightmap() - .onlyInBiome() - .build(); - } - } - - /** - * Will create feature which will be generated once in each chunk. - * @param id {@link ResourceLocation} feature ID. - * @param decoration {@link Decoration} feature step. - * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. - * @return new BCLFeature instance. - */ - public static BCLFeature makeChunkFeature(ResourceLocation id, Decoration decoration, Feature feature) { - return BCLFeatureBuilder.start(id, feature).decoration(decoration).count(1).onlyInBiome().build(); - } - - /** - * Will create feature with chanced decoration, chance for feature to generate per chunk is 1 / chance. - * @param id {@link ResourceLocation} feature ID. - * @param decoration {@link Decoration} feature step. - * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. - * @param chance chance for feature to be generated in. - * @return new BCLFeature instance. - */ - public static BCLFeature makeChancedFeature(ResourceLocation id, Decoration decoration, Feature feature, int chance) { - return BCLFeatureBuilder.start(id, feature).decoration(decoration).oncePerChunks(chance).squarePlacement().onlyInBiome().build(); - } - - /** - * Will create feature with specified generation iterations per chunk. - * @param id {@link ResourceLocation} feature ID. - * @param decoration {@link Decoration} feature step. - * @param feature {@link Feature} with {@link NoneFeatureConfiguration} config. - * @param count iterations steps. - * @return new BCLFeature instance. - */ - public static BCLFeature makeCountFeature(ResourceLocation id, Decoration decoration, Feature feature, int count) { - return BCLFeatureBuilder.start(id, feature).decoration(decoration).count(count).squarePlacement().onlyInBiome().build(); - } - - /** - * Will create a basic ore feature. - * - * @param id {@link ResourceLocation} feature ID. - * @param blockOre {@link Decoration} feature step. - * @param hostBlock {@link Block} to generate feature in. - * @param veins iterations per chunk. - * @param veinSize size of ore vein. - * @param airDiscardChance chance that this orge gets discarded when it is exposed to air - * @param placement {@link net.minecraft.world.level.levelgen.placement.PlacementModifier} for the ore distribution, - * for example {@code PlacementUtils.FULL_RANGE}, {@code PlacementUtils.RANGE_10_10} - * @param rare when true, this is placed as a rare resource - * @return new BCLFeature instance. - */ - public static BCLFeature makeOreFeature(ResourceLocation id, Block blockOre, Block hostBlock, int veins, int veinSize, float airDiscardChance, PlacementModifier placement, boolean rare) { - BCLFeatureBuilder builder = BCLFeatureBuilder.start(id, Feature.ORE).decoration(Decoration.UNDERGROUND_ORES); - - if (rare) { - builder.oncePerChunks(veins); - } - else { - builder.count(veins); - } - - builder.modifier(placement).squarePlacement().onlyInBiome(); - - return builder.build(new OreConfiguration( - new BlockMatchTest(hostBlock), - blockOre.defaultBlockState(), - veinSize, - airDiscardChance - )); - } -} diff --git a/src/main/java/ru/bclib/api/features/BCLFeatureBuilder.java b/src/main/java/ru/bclib/api/features/BCLFeatureBuilder.java deleted file mode 100644 index ab710c0b..00000000 --- a/src/main/java/ru/bclib/api/features/BCLFeatureBuilder.java +++ /dev/null @@ -1,154 +0,0 @@ -package ru.bclib.api.features; - -import net.minecraft.data.worldgen.placement.PlacementUtils; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.valueproviders.UniformInt; -import net.minecraft.world.level.levelgen.GenerationStep.Decoration; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; -import net.minecraft.world.level.levelgen.placement.BiomeFilter; -import net.minecraft.world.level.levelgen.placement.CountOnEveryLayerPlacement; -import net.minecraft.world.level.levelgen.placement.CountPlacement; -import net.minecraft.world.level.levelgen.placement.InSquarePlacement; -import net.minecraft.world.level.levelgen.placement.PlacementModifier; -import net.minecraft.world.level.levelgen.placement.RarityFilter; -import ru.bclib.world.features.BCLFeature; - -import java.util.ArrayList; -import java.util.List; - -public class BCLFeatureBuilder >{ - private static final BCLFeatureBuilder INSTANCE = new BCLFeatureBuilder(); - private List modifications = new ArrayList<>(16); - private ResourceLocation featureID; - private Decoration decoration; - private F feature; - - private BCLFeatureBuilder() {} - - /** - * Starts a new {@link BCLFeature} builder. - * @param featureID {@link ResourceLocation} feature identifier. - * @param feature {@link Feature} to construct. - * @return {@link BCLFeatureBuilder} instance. - */ - public static BCLFeatureBuilder start(ResourceLocation featureID, Feature feature) { - INSTANCE.decoration = Decoration.VEGETAL_DECORATION; - INSTANCE.modifications.clear(); - INSTANCE.featureID = featureID; - INSTANCE.feature = feature; - return INSTANCE; - } - - /** - * Set generation step for the feature. Default is {@code VEGETAL_DECORATION}. - * @param decoration {@link Decoration} step. - * @return same {@link BCLFeatureBuilder} instance. - */ - public BCLFeatureBuilder decoration(Decoration decoration) { - this.decoration = decoration; - return this; - } - - /** - * Add feature placement modifier. Used as a condition for feature how to generate. - * @param modifier {@link PlacementModifier}. - * @return same {@link BCLFeatureBuilder} instance. - */ - public BCLFeatureBuilder modifier(PlacementModifier modifier) { - modifications.add(modifier); - return this; - } - - public BCLFeatureBuilder modifier(List modifiers) { - modifications.addAll(modifiers); - return this; - } - - /** - * Generate feature in certain iterations (per chunk). - * @param count how many times feature will be generated in chunk. - * @return same {@link BCLFeatureBuilder} instance. - */ - public BCLFeatureBuilder count(int count) { - return modifier(CountPlacement.of(count)); - } - - /** - * Generate feature in certain iterations (per chunk). Count can be between 0 and max value. - * @param count maximum amount of iterations per chunk. - * @return same {@link BCLFeatureBuilder} instance. - */ - public BCLFeatureBuilder countMax(int count) { - return modifier(CountPlacement.of(UniformInt.of(0, count))); - } - - /** - * Generate feature in certain iterations (per chunk). - * Feature will be generated on all layers (example - Nether plants). - * @param count how many times feature will be generated in chunk layers. - * @return same {@link BCLFeatureBuilder} instance. - */ - @SuppressWarnings("deprecation") - public BCLFeatureBuilder countLayers(int count) { - return modifier(CountOnEveryLayerPlacement.of(count)); - } - - /** - * Generate feature in certain iterations (per chunk). Count can be between 0 and max value. - * Feature will be generated on all layers (example - Nether plants). - * @param count maximum amount of iterations per chunk layers. - * @return same {@link BCLFeatureBuilder} instance. - */ - @SuppressWarnings("deprecation") - public BCLFeatureBuilder countLayersMax(int count) { - return modifier(CountOnEveryLayerPlacement.of(UniformInt.of(0, count))); - } - - /** - * Will place feature once in certain amount of chunks (in average). - * @param chunks amount of chunks. - * @return same {@link BCLFeatureBuilder} instance. - */ - public BCLFeatureBuilder oncePerChunks(int chunks) { - return modifier(RarityFilter.onAverageOnceEvery(chunks)); - } - - /** - * Restricts feature generation only to biome where feature was added. - * @return same {@link BCLFeatureBuilder} instance. - */ - public BCLFeatureBuilder onlyInBiome() { - return modifier(BiomeFilter.biome()); - } - - // Are these two things required in 1.18.1? - // TODO - add information - public BCLFeatureBuilder squarePlacement() { - return modifier(InSquarePlacement.spread()); - } - - // TODO - add information - public BCLFeatureBuilder heightmap() { - return modifier(PlacementUtils.HEIGHTMAP); - } - - /** - * Builds a new {@link BCLFeature} instance. Features will be registered during this process. - * @param configuration any {@link FeatureConfiguration} for provided {@link Feature}. - * @return created {@link BCLFeature} instance. - */ - public BCLFeature build(FC configuration) { - PlacementModifier [] modifiers = modifications.toArray(new PlacementModifier [modifications.size()]); - return new BCLFeature(featureID, feature, decoration, configuration, modifiers); - } - - /** - * Builds a new {@link BCLFeature} instance with {@code NONE} {@link FeatureConfiguration}. - * Features will be registered during this process. - * @return created {@link BCLFeature} instance. - */ - public BCLFeature build() { - return build((FC)FeatureConfiguration.NONE); - } -} diff --git a/src/main/java/ru/bclib/api/spawning/SpawnRuleBuilder.java b/src/main/java/ru/bclib/api/spawning/SpawnRuleBuilder.java deleted file mode 100644 index e8acc908..00000000 --- a/src/main/java/ru/bclib/api/spawning/SpawnRuleBuilder.java +++ /dev/null @@ -1,335 +0,0 @@ -package ru.bclib.api.spawning; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.mixin.object.builder.SpawnRestrictionAccessor; -import net.minecraft.core.BlockPos; -import net.minecraft.world.Difficulty; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.SpawnPlacements.SpawnPredicate; -import net.minecraft.world.entity.SpawnPlacements.Type; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.levelgen.Heightmap.Types; -import net.minecraft.world.phys.AABB; -import ru.bclib.entity.BCLEntityWrapper; -import ru.bclib.interfaces.SpawnRule; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -public class SpawnRuleBuilder { - private static final Map RULES_CACHE = Maps.newHashMap(); - private static final SpawnRuleBuilder INSTANCE = new SpawnRuleBuilder(); - private List rules = Lists.newArrayList(); - private SpawnRuleEntry entryInstance; - private EntityType entityType; - - private SpawnRuleBuilder() {} - - /** - * Starts new rule building process. - * @param entityType The entity you want to build a rule for - * @return prepared {@link SpawnRuleBuilder} instance. - */ - public static SpawnRuleBuilder start(EntityType entityType) { - INSTANCE.entityType = entityType; - INSTANCE.rules.clear(); - return INSTANCE; - } - - /** - * Starts new rule building process. - * @param wrapper The entity you want to build a rule for - * @return prepared {@link SpawnRuleBuilder} instance. - */ - public static SpawnRuleBuilder start(BCLEntityWrapper wrapper) { - SpawnRuleBuilder builder = start(wrapper.type()); - if (!wrapper.canSpawn()){ - builder.preventSpawn(); - } - return builder; - } - - /** - * Stop entity spawn entierly - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder preventSpawn() { - entryInstance = getFromCache("prevent", () -> { - return new SpawnRuleEntry(-1, (type, world, spawnReason, pos, random) -> false); - }); - rules.add(entryInstance); - return this; - } - - /** - * Stop entity spawn on peaceful {@link Difficulty} - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder notPeaceful() { - entryInstance = getFromCache("not_peaceful", () -> { - return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> world.getDifficulty() != Difficulty.PEACEFUL); - }); - rules.add(entryInstance); - return this; - } - - /** - * Restricts entity spawn above world surface (flying mobs). - * @param minHeight minimal spawn height. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder aboveGround(int minHeight) { - entryInstance = getFromCache("above_ground", () -> { - return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> { - if (pos.getY() < world.getMinBuildHeight() + 2) { - return false; - } - return pos.getY() > world.getHeight(Types.WORLD_SURFACE, pos.getX(), pos.getZ()) + minHeight; - }); - }); - rules.add(entryInstance); - return this; - } - - /** - * Restricts entity spawn below world logical height (useful for Nether mobs). - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder belowMaxHeight() { - entryInstance = getFromCache("below_max_height", () -> { - return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> pos.getY() < world.dimensionType().logicalHeight()); - }); - rules.add(entryInstance); - return this; - } - - /** - * Restricts spawning only to vanilla valid blocks. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder onlyOnValidBlocks() { - entryInstance = getFromCache("only_on_valid_blocks", () -> { - return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> { - BlockPos below = pos.below(); - return world.getBlockState(below).isValidSpawn(world, below, type); - }); - }); - rules.add(entryInstance); - return this; - } - - /** - * Restricts spawning only to specified blocks. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder onlyOnBlocks(Block... blocks) { - final Block[] floorBlocks = blocks; - Arrays.sort(floorBlocks, Comparator.comparing(Block::getDescriptionId)); - - StringBuilder builder = new StringBuilder("only_on_blocks"); - for (Block block : floorBlocks) { - builder.append('_'); - builder.append(block.getDescriptionId()); - } - - entryInstance = getFromCache(builder.toString(), () -> { - return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> { - Block below = world.getBlockState(pos.below()).getBlock(); - for (Block floor: floorBlocks) { - if (floor == below) { - return true; - } - } - return false; - }); - }); - - rules.add(entryInstance); - return this; - } - - /** - * Will spawn entity with 1 / chance probability (randomly). - * @param chance probability limit. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder withChance(int chance) { - entryInstance = getFromCache("with_chance_" + chance, () -> { - return new SpawnRuleEntry(1, (type, world, spawnReason, pos, random) -> random.nextInt(chance) == 0); - }); - rules.add(entryInstance); - return this; - } - - /** - * Will spawn entity only below specified brightness value. - * @param lightLevel light level upper limit. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder belowBrightness(int lightLevel) { - entryInstance = getFromCache("below_brightness_" + lightLevel, () -> { - return new SpawnRuleEntry(2, (type, world, spawnReason, pos, random) -> world.getMaxLocalRawBrightness(pos) <= lightLevel); - }); - rules.add(entryInstance); - return this; - } - - /** - * Will spawn entity only above specified brightness value. - * @param lightLevel light level lower limit. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder aboveBrightness(int lightLevel) { - entryInstance = getFromCache("above_brightness_" + lightLevel, () -> { - return new SpawnRuleEntry(2, (type, world, spawnReason, pos, random) -> world.getMaxLocalRawBrightness(pos) >= lightLevel); - }); - rules.add(entryInstance); - return this; - } - - /** - * Entity spawn will follow common vanilla spawn rules - spawn in darkness and not on peaceful level. - * @param lightLevel light level upper limit. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder hostile(int lightLevel) { - return notPeaceful().belowBrightness(lightLevel); - } - - /** - * Entity spawn will follow common vanilla spawn rules - spawn in darkness (below light level 7) and not on peaceful level. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder vanillaHostile() { - return hostile(7); - } - - /** - * Will spawn entity only if count of nearby entities will be lower than specified. - * @param selectorType selector {@link EntityType} to search. - * @param count max entity count. - * @param side side of box to search in. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder maxNearby(EntityType selectorType, int count, int side) { - final Class baseClass = selectorType.getBaseClass(); - entryInstance = getFromCache("max_nearby_" + selectorType.getDescriptionId()+"_"+count+"_"+side, () -> { - return new SpawnRuleEntry(3, (type, world, spawnReason, pos, random) -> { - try { - final AABB box = new AABB(pos).inflate(side, world.getHeight(), side); - final List list = world.getEntitiesOfClass(baseClass, box, (entity) -> true); - return list.size() < count; - } - catch (Exception e) { - return true; - } - }); - }); - rules.add(entryInstance); - return this; - } - - /** - * Will spawn entity only if count of nearby entities with same type will be lower than specified. - * @param count max entity count. - * @param side side of box to search in. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder maxNearby(int count, int side) { - return maxNearby(entityType, count, side); - } - - /** - * Will spawn entity only if count of nearby entities with same type will be lower than specified. - * @param count max entity count. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder maxNearby(int count) { - return maxNearby(entityType, count, 256); - } - - /** - * Allows to add custom spawning rule for specific entities. - * @param rule {@link SpawnRule} rule, can be a lambda expression. - * @return same {@link SpawnRuleBuilder} instance. - */ - public SpawnRuleBuilder customRule(SpawnRule rule) { - rules.add(new SpawnRuleEntry(7, rule)); - return this; - } - - /** - * Finalize spawning rule creation. - * @param spawnType {@link Type} of spawn. - * @param heightmapType {@link Types} heightmap type. - */ - public void build(Type spawnType, Types heightmapType) { - final List rulesCopy = Lists.newArrayList(this.rules); - Collections.sort(rulesCopy); - - SpawnPredicate predicate = (entityType, serverLevelAccessor, mobSpawnType, blockPos, random) -> { - for (SpawnRuleEntry rule: rulesCopy) { - if (!rule.canSpawn(entityType, serverLevelAccessor, mobSpawnType, blockPos, random)) { - return false; - } - } - return true; - }; - - SpawnRestrictionAccessor.callRegister(entityType, spawnType, heightmapType, predicate); - } - - /** - * Finalize spawning rule creation with No Restrictions spawn type, useful for flying entities. - * @param heightmapType {@link Types} heightmap type. - */ - public void buildNoRestrictions(Types heightmapType) { - build(Type.NO_RESTRICTIONS, heightmapType); - } - - /** - * Finalize spawning rule creation with On Ground spawn type, useful for common entities. - * @param heightmapType {@link Types} heightmap type. - */ - public void buildOnGround(Types heightmapType) { - build(Type.ON_GROUND, heightmapType); - } - - /** - * Finalize spawning rule creation with In Water spawn type, useful for water entities. - * @param heightmapType {@link Types} heightmap type. - */ - public void buildInWater(Types heightmapType) { - build(Type.IN_WATER, heightmapType); - } - - /** - * Finalize spawning rule creation with In Lava spawn type, useful for lava entities. - * @param heightmapType {@link Types} heightmap type. - */ - public void buildInLava(Types heightmapType) { - build(Type.IN_LAVA, heightmapType); - } - - /** - * Internal function, will take entry from cache or create it if necessary. - * @param name {@link String} entry internal name. - * @param supplier {@link Supplier} for {@link SpawnRuleEntry}. - * @return new or existing {@link SpawnRuleEntry}. - */ - private static SpawnRuleEntry getFromCache(String name, Supplier supplier) { - SpawnRuleEntry entry = RULES_CACHE.get(name); - if (entry == null) { - entry = supplier.get(); - RULES_CACHE.put(name, entry); - } - return entry; - } -} diff --git a/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java b/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java deleted file mode 100644 index 778f6fc8..00000000 --- a/src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.bclib.api.spawning; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobSpawnType; -import net.minecraft.world.level.LevelAccessor; -import org.jetbrains.annotations.NotNull; -import ru.bclib.interfaces.SpawnRule; - -import java.util.Random;import net.minecraft.util.RandomSource; - -public class SpawnRuleEntry implements Comparable { - private final SpawnRule rule; - private final byte priority; - - public SpawnRuleEntry(int priority, SpawnRule rule) { - this.priority = (byte) priority; - this.rule = rule; - } - - protected boolean canSpawn(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - return rule.canSpawn(type, world, spawnReason, pos, random); - } - - @Override - public int compareTo(@NotNull SpawnRuleEntry entry) { - return Integer.compare(priority, entry.priority); - } -} diff --git a/src/main/java/ru/bclib/api/surface/SurfaceRuleBuilder.java b/src/main/java/ru/bclib/api/surface/SurfaceRuleBuilder.java deleted file mode 100644 index 2e6a14c5..00000000 --- a/src/main/java/ru/bclib/api/surface/SurfaceRuleBuilder.java +++ /dev/null @@ -1,273 +0,0 @@ -package ru.bclib.api.surface; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.SurfaceRules; -import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; -import net.minecraft.world.level.levelgen.placement.CaveSurface; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.api.surface.rules.NoiseCondition; -import ru.bclib.world.surface.DoubleBlockSurfaceNoiseCondition; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -public class SurfaceRuleBuilder { - private static final Map RULES_CACHE = Maps.newHashMap(); - private static final SurfaceRuleBuilder INSTANCE = new SurfaceRuleBuilder(); - private List rules = Lists.newArrayList(); - private SurfaceRuleEntry entryInstance; - private ResourceKey biomeKey; - - private SurfaceRuleBuilder() {} - - public static SurfaceRuleBuilder start() { - INSTANCE.biomeKey = null; - INSTANCE.rules.clear(); - return INSTANCE; - } - - /** - * Restricts surface to only one biome. - * @param biomeKey {@link ResourceKey} for the {@link Biome}. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder biome(ResourceKey biomeKey) { - this.biomeKey = biomeKey; - return this; - } - - /** - * Restricts surface to only one biome. - * @param biome {@link Biome}. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder biome(Biome biome) { - return biome(BiomeAPI.getBiomeKey(biome)); - } - - /** - * Set biome surface with specified {@link BlockState}. Example - block of grass in the Overworld biomes - * @param state {@link BlockState} for the ground cover. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder surface(BlockState state) { - entryInstance = getFromCache("surface_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - rule = SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, rule); - return new SurfaceRuleEntry(2, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Set biome subsurface with specified {@link BlockState}. Example - dirt in the Overworld biomes. - * @param state {@link BlockState} for the subterrain layer. - * @param depth block layer depth. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder subsurface(BlockState state, int depth) { - entryInstance = getFromCache("subsurface_" + depth + "_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(depth, false, CaveSurface.FLOOR), rule); - return new SurfaceRuleEntry(3, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Set biome filler with specified {@link BlockState}. Example - stone in the Overworld biomes. The rule is added with priority 10. - * @param state {@link BlockState} for filling. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder filler(BlockState state) { - entryInstance = getFromCache("fill_" + state.toString(), () -> new SurfaceRuleEntry(10, SurfaceRules.state(state))); - rules.add(entryInstance); - return this; - } - - /** - * Set biome floor with specified {@link BlockState}. Example - underside of a gravel floor. The rule is added with priority 3. - * @param state {@link BlockState} for the ground cover. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder floor(BlockState state) { - entryInstance = getFromCache("floor_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - return new SurfaceRuleEntry(3, SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, rule)); - }); - rules.add(entryInstance); - return this; - } - - /** - * Set biome floor material with specified {@link BlockState} and height. The rule is added with priority 3. - * @param state {@link BlockState} for the subterrain layer. - * @param height block layer height. - * @param noise The noise object that is applied - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder belowFloor(BlockState state, int height, NoiseCondition noise) { - entryInstance = getFromCache("below_floor_" + height + "_" + state.toString() + "_" + noise.getClass().getSimpleName(), () -> { - RuleSource rule = SurfaceRules.state(state); - rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(height, false, CaveSurface.FLOOR), SurfaceRules.ifTrue(noise, rule)); - return new SurfaceRuleEntry(3, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Set biome floor material with specified {@link BlockState} and height. The rule is added with priority 3. - * @param state {@link BlockState} for the subterrain layer. - * @param height block layer height. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder belowFloor(BlockState state, int height) { - entryInstance = getFromCache("below_floor_" + height + "_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(height, false, CaveSurface.FLOOR), rule); - return new SurfaceRuleEntry(3, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Set biome ceiling with specified {@link BlockState}. Example - block of sandstone in the Overworld desert in air pockets. The rule is added with priority 3. - * @param state {@link BlockState} for the ground cover. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder ceil(BlockState state) { - entryInstance = getFromCache("ceil_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - return new SurfaceRuleEntry(3, SurfaceRules.ifTrue(SurfaceRules.ON_CEILING, rule)); - }); - rules.add(entryInstance); - return this; - } - - /** - * Set biome ceiling material with specified {@link BlockState} and height. Example - sandstone in the Overworld deserts. The rule is added with priority 3. - * @param state {@link BlockState} for the subterrain layer. - * @param height block layer height. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder aboveCeil(BlockState state, int height) { - entryInstance = getFromCache("above_ceil_" + height + "_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(height, false, CaveSurface.CEILING), rule); - return new SurfaceRuleEntry(3, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Will cover steep areas (with large terrain angle). Example - Overworld mountains. - * @param state {@link BlockState} for the steep layer. - * @param depth layer depth - * @return - */ - public SurfaceRuleBuilder steep(BlockState state, int depth) { - entryInstance = getFromCache("steep_" + depth + "_" + state.toString(), () -> { - RuleSource rule = SurfaceRules.state(state); - rule = SurfaceRules.ifTrue(SurfaceRules.stoneDepthCheck(depth, false, CaveSurface.FLOOR), rule); - rule = SurfaceRules.ifTrue(SurfaceRules.steep(), rule); - int priority = depth < 1 ? 0 : 1; - return new SurfaceRuleEntry(priority, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Allows to add custom rule. - * @param priority rule priority, lower values = higher priority (rule will be applied before others). - * @param rule custom {@link SurfaceRules.RuleSource}. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder rule(int priority, SurfaceRules.RuleSource rule) { - rules.add(new SurfaceRuleEntry(priority, rule)); - return this; - } - - /** - * Allows to add custom rule. - * @param rule custom {@link SurfaceRules.RuleSource}. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder rule(SurfaceRules.RuleSource rule) { - return rule(7, rule); - } - - /** - * Set biome floor with specified {@link BlockState} and the {@link DoubleBlockSurfaceNoiseCondition}. The rule is added with priority 3. - * @param surfaceBlockA {@link BlockState} for the ground cover. - * @param surfaceBlockB {@link BlockState} for the alternative ground cover. - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder chancedFloor(BlockState surfaceBlockA, BlockState surfaceBlockB){ - return chancedFloor(surfaceBlockA, surfaceBlockB, DoubleBlockSurfaceNoiseCondition.CONDITION); - } - - /** - * Set biome floor with specified {@link BlockState} and the given Noise Function. The rule is added with priority 3. - * @param surfaceBlockA {@link BlockState} for the ground cover. - * @param surfaceBlockB {@link BlockState} for the alternative ground cover. - * @param noise The {@link NoiseCondition} - * @return same {@link SurfaceRuleBuilder} instance. - */ - public SurfaceRuleBuilder chancedFloor(BlockState surfaceBlockA, BlockState surfaceBlockB, NoiseCondition noise){ - entryInstance = getFromCache("chancedFloor_" + surfaceBlockA + "_" + surfaceBlockB + "_" + noise.getClass().getSimpleName(), () -> { - RuleSource rule = - SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, - SurfaceRules.sequence( - SurfaceRules.ifTrue(noise, SurfaceRules.state(surfaceBlockA)), - SurfaceRules.state(surfaceBlockB) - ) - ) - ; - return new SurfaceRuleEntry(4, rule); - }); - rules.add(entryInstance); - return this; - } - - /** - * Finalise rule building process. - * @return {@link SurfaceRules.RuleSource}. - */ - public SurfaceRules.RuleSource build() { - Collections.sort(rules); - List ruleList = rules.stream().map(entry -> entry.getRule()).toList(); - SurfaceRules.RuleSource[] ruleArray = ruleList.toArray(new SurfaceRules.RuleSource[ruleList.size()]); - SurfaceRules.RuleSource rule = SurfaceRules.sequence(ruleArray); - if (biomeKey != null) { - rule = SurfaceRules.ifTrue(SurfaceRules.isBiome(biomeKey), rule); - } - return rule; - } - - /** - * Internal function, will take entry from cache or create it if necessary. - * @param name {@link String} entry internal name. - * @param supplier {@link Supplier} for {@link SurfaceRuleEntry}. - * @return new or existing {@link SurfaceRuleEntry}. - */ - private static SurfaceRuleEntry getFromCache(String name, Supplier supplier) { - SurfaceRuleEntry entry = RULES_CACHE.get(name); - if (entry == null) { - entry = supplier.get(); - RULES_CACHE.put(name, entry); - } - return entry; - } -} diff --git a/src/main/java/ru/bclib/api/surface/SurfaceRuleEntry.java b/src/main/java/ru/bclib/api/surface/SurfaceRuleEntry.java deleted file mode 100644 index 7255631c..00000000 --- a/src/main/java/ru/bclib/api/surface/SurfaceRuleEntry.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.bclib.api.surface; - -import net.minecraft.world.entity.Mob; -import net.minecraft.world.level.levelgen.SurfaceRules; -import org.jetbrains.annotations.NotNull; - -public class SurfaceRuleEntry implements Comparable { - private final SurfaceRules.RuleSource rule; - private final byte priority; - - public SurfaceRuleEntry(int priority, SurfaceRules.RuleSource rule) { - this.priority = (byte) priority; - this.rule = rule; - } - - protected SurfaceRules.RuleSource getRule() { - return rule; - } - - @Override - public int compareTo(@NotNull SurfaceRuleEntry entry) { - return Integer.compare(priority, entry.priority); - } -} diff --git a/src/main/java/ru/bclib/api/surface/rules/NoiseCondition.java b/src/main/java/ru/bclib/api/surface/rules/NoiseCondition.java deleted file mode 100644 index e8847356..00000000 --- a/src/main/java/ru/bclib/api/surface/rules/NoiseCondition.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.bclib.api.surface.rules; - -import net.minecraft.world.level.levelgen.SurfaceRules; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; - -public interface NoiseCondition extends SurfaceRules.ConditionSource{ - boolean test(SurfaceRulesContextAccessor context); -} diff --git a/src/main/java/ru/bclib/api/surface/rules/RandomIntProvider.java b/src/main/java/ru/bclib/api/surface/rules/RandomIntProvider.java deleted file mode 100644 index b6a386f6..00000000 --- a/src/main/java/ru/bclib/api/surface/rules/RandomIntProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.bclib.api.surface.rules; - -import com.mojang.serialization.Codec; -import net.minecraft.core.Registry; -import ru.bclib.BCLib; -import ru.bclib.interfaces.NumericProvider; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; -import ru.bclib.util.MHelper; - -public record RandomIntProvider(int range) implements NumericProvider { - public static final Codec CODEC = Codec.INT.fieldOf("range").xmap(RandomIntProvider::new, obj -> obj.range).codec(); - - @Override - public int getNumber(SurfaceRulesContextAccessor context) { - return MHelper.RANDOM.nextInt(range); - } - - @Override - public Codec pcodec() { - return CODEC; - } - - static { - Registry.register(NumericProvider.NUMERIC_PROVIDER , BCLib.makeID("rnd_int"), RandomIntProvider.CODEC); - } -} diff --git a/src/main/java/ru/bclib/api/surface/rules/SurfaceNoiseCondition.java b/src/main/java/ru/bclib/api/surface/rules/SurfaceNoiseCondition.java deleted file mode 100644 index d5fda0d5..00000000 --- a/src/main/java/ru/bclib/api/surface/rules/SurfaceNoiseCondition.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.bclib.api.surface.rules; - -import net.minecraft.world.level.levelgen.SurfaceRules.Condition; -import net.minecraft.world.level.levelgen.SurfaceRules.Context; -import net.minecraft.world.level.levelgen.SurfaceRules.LazyXZCondition; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; - -public abstract class SurfaceNoiseCondition implements NoiseCondition{ - @Override - public final Condition apply(Context context2) { - final SurfaceNoiseCondition self = this; - - class Generator extends LazyXZCondition { - Generator() { - super(context2); - } - - @Override - protected boolean compute() { - final SurfaceRulesContextAccessor context = SurfaceRulesContextAccessor.class.cast(this.context); - if (context==null) return false; - return self.test(context); - } - } - - return new Generator(); - } - - public abstract boolean test(SurfaceRulesContextAccessor context); -} diff --git a/src/main/java/ru/bclib/api/surface/rules/SwitchRuleSource.java b/src/main/java/ru/bclib/api/surface/rules/SwitchRuleSource.java deleted file mode 100644 index 18cdb790..00000000 --- a/src/main/java/ru/bclib/api/surface/rules/SwitchRuleSource.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.bclib.api.surface.rules; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.Registry; -import net.minecraft.util.KeyDispatchDataCodec; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.SurfaceRules.Context; -import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; -import net.minecraft.world.level.levelgen.SurfaceRules.SurfaceRule; -import org.jetbrains.annotations.Nullable; -import ru.bclib.interfaces.NumericProvider; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; - -import java.util.List; - -// -public record SwitchRuleSource(NumericProvider selector, List collection) implements RuleSource { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - NumericProvider.CODEC.fieldOf("selector").forGetter(SwitchRuleSource::selector), - RuleSource.CODEC.listOf().fieldOf("collection").forGetter(SwitchRuleSource::collection) - ).apply(instance, SwitchRuleSource::new)); - - private static final KeyDispatchDataCodec KEY_CODEC = KeyDispatchDataCodec.of(SwitchRuleSource.CODEC); - - @Override - public KeyDispatchDataCodec codec() { - return KEY_CODEC; - } - - @Override - public SurfaceRule apply(Context context) { - - return new SurfaceRule() { - @Nullable - @Override - public BlockState tryApply(int x, int y, int z) { - final SurfaceRulesContextAccessor ctx = SurfaceRulesContextAccessor.class.cast(context); - int nr = Math.max(0, selector.getNumber(ctx)) % collection.size(); - - return collection.get(nr).apply(context).tryApply(x, y, z); - } - }; - } - - static { - Registry.register(Registry.RULE, "bclib_switch_rule", SwitchRuleSource.CODEC); - } -} diff --git a/src/main/java/ru/bclib/api/surface/rules/VolumeNoiseCondition.java b/src/main/java/ru/bclib/api/surface/rules/VolumeNoiseCondition.java deleted file mode 100644 index 7ce773bb..00000000 --- a/src/main/java/ru/bclib/api/surface/rules/VolumeNoiseCondition.java +++ /dev/null @@ -1,40 +0,0 @@ -package ru.bclib.api.surface.rules; - -import net.minecraft.util.KeyDispatchDataCodec; -import net.minecraft.world.level.levelgen.SurfaceRules.Condition; -import net.minecraft.world.level.levelgen.SurfaceRules.ConditionSource; -import net.minecraft.world.level.levelgen.SurfaceRules.Context; -import net.minecraft.world.level.levelgen.SurfaceRules.LazyCondition; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; - -public abstract class VolumeNoiseCondition implements NoiseCondition{ - public abstract KeyDispatchDataCodec codec(); - - @Override - public final Condition apply(Context context2) { - final VolumeNoiseCondition self = this; - - class Generator extends LazyCondition { - Generator() { - super(context2); - } - - @Override - protected long getContextLastUpdate() { - final SurfaceRulesContextAccessor ctx = SurfaceRulesContextAccessor.class.cast(this.context); - return ctx.getLastUpdateY() + ctx.getLastUpdateXZ(); - } - - @Override - protected boolean compute() { - final SurfaceRulesContextAccessor context = SurfaceRulesContextAccessor.class.cast(this.context); - if (context==null) return false; - return self.test(context); - } - } - - return new Generator(); - } - - public abstract boolean test(SurfaceRulesContextAccessor context); -} diff --git a/src/main/java/ru/bclib/api/tag/CommonBlockTags.java b/src/main/java/ru/bclib/api/tag/CommonBlockTags.java deleted file mode 100644 index 25b8a388..00000000 --- a/src/main/java/ru/bclib/api/tag/CommonBlockTags.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.bclib.api.tag; - -import net.minecraft.tags.TagKey; -import net.minecraft.world.level.block.Block; - -public class CommonBlockTags { - public static final TagKey BARREL = TagAPI.makeCommonBlockTag("barrel"); - public static final TagKey BOOKSHELVES = TagAPI.makeCommonBlockTag("bookshelves"); - public static final TagKey CHEST = TagAPI.makeCommonBlockTag("chest"); - public static final TagKey END_STONES = TagAPI.makeCommonBlockTag("end_stones"); - public static final TagKey GEN_END_STONES = END_STONES; - public static final TagKey IMMOBILE = TagAPI.makeCommonBlockTag("immobile"); - public static final TagKey LEAVES = TagAPI.makeCommonBlockTag("leaves"); - public static final TagKey NETHERRACK = TagAPI.makeCommonBlockTag("netherrack"); - public static final TagKey NETHER_MYCELIUM = TagAPI.makeCommonBlockTag("nether_mycelium"); - public static final TagKey NETHER_PORTAL_FRAME = TagAPI.makeCommonBlockTag("nether_pframe"); - public static final TagKey NETHER_STONES = TagAPI.makeCommonBlockTag("nether_stones"); - public static final TagKey SAPLINGS = TagAPI.makeCommonBlockTag("saplings"); - public static final TagKey SOUL_GROUND = TagAPI.makeCommonBlockTag("soul_ground"); - public static final TagKey WOODEN_BARREL = TagAPI.makeCommonBlockTag("wooden_barrels"); - public static final TagKey WOODEN_CHEST = TagAPI.makeCommonBlockTag("wooden_chests"); - public static final TagKey WORKBENCHES = TagAPI.makeCommonBlockTag("workbench"); - - public static final TagKey DRAGON_IMMUNE = TagAPI.makeCommonBlockTag("dragon_immune"); - - public static final TagKey MINABLE_WITH_HAMMER = TagAPI.makeCommonBlockTag("mineable/hammer"); -} diff --git a/src/main/java/ru/bclib/api/tag/CommonItemTags.java b/src/main/java/ru/bclib/api/tag/CommonItemTags.java deleted file mode 100644 index eb96d373..00000000 --- a/src/main/java/ru/bclib/api/tag/CommonItemTags.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.bclib.api.tag; - -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; - -public class CommonItemTags { - public final static TagKey HAMMERS = TagAPI.makeCommonItemTag("hammers"); - public static final TagKey BARREL = TagAPI.makeCommonItemTag("barrel"); - public static final TagKey CHEST = TagAPI.makeCommonItemTag("chest"); - public static final TagKey SHEARS = TagAPI.makeCommonItemTag("shears"); - public static final TagKey FURNACES = TagAPI.makeCommonItemTag("furnaces"); - public static final TagKey IRON_INGOTS = TagAPI.makeCommonItemTag("iron_ingots"); - public static final TagKey LEAVES = TagAPI.makeCommonItemTag("leaves"); - public static final TagKey SAPLINGS = TagAPI.makeCommonItemTag("saplings"); - public static final TagKey SOUL_GROUND = TagAPI.makeCommonItemTag("soul_ground"); - public static final TagKey WOODEN_BARREL = TagAPI.makeCommonItemTag("wooden_barrels"); - public static final TagKey WOODEN_CHEST = TagAPI.makeCommonItemTag("wooden_chests"); - public static final TagKey WORKBENCHES = TagAPI.makeCommonItemTag("workbench"); -} diff --git a/src/main/java/ru/bclib/api/tag/NamedBlockTags.java b/src/main/java/ru/bclib/api/tag/NamedBlockTags.java deleted file mode 100644 index d7a3779a..00000000 --- a/src/main/java/ru/bclib/api/tag/NamedBlockTags.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.bclib.api.tag; - -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.TagKey; -import net.minecraft.world.level.block.Block; - - -public class NamedBlockTags { - public static final TagKey ANVIL = BlockTags.ANVIL; - public static final TagKey BUTTONS = BlockTags.BUTTONS; - public static final TagKey CLIMBABLE = BlockTags.CLIMBABLE; - public static final TagKey DOORS = BlockTags.DOORS; - public static final TagKey FENCES = BlockTags.FENCES; - public static final TagKey FENCE_GATES = BlockTags.FENCE_GATES; - public static final TagKey LEAVES = BlockTags.LEAVES; - public static final TagKey LOGS = BlockTags.LOGS; - public static final TagKey LOGS_THAT_BURN = BlockTags.LOGS_THAT_BURN; - public static final TagKey NYLIUM = BlockTags.NYLIUM; - public static final TagKey PLANKS = BlockTags.PLANKS; - public static final TagKey PRESSURE_PLATES = BlockTags.PRESSURE_PLATES; - public static final TagKey SAPLINGS = BlockTags.SAPLINGS; - public static final TagKey SIGNS = BlockTags.SIGNS; - public static final TagKey SLABS = BlockTags.SLABS; - public static final TagKey STAIRS = BlockTags.STAIRS; - public static final TagKey STONE_PRESSURE_PLATES = BlockTags.STONE_PRESSURE_PLATES; - public static final TagKey TRAPDOORS = BlockTags.TRAPDOORS; - public static final TagKey WALLS = BlockTags.WALLS; - public static final TagKey WOODEN_BUTTONS = BlockTags.WOODEN_BUTTONS; - public static final TagKey WOODEN_DOORS = BlockTags.WOODEN_DOORS; - public static final TagKey WOODEN_FENCES = BlockTags.WOODEN_FENCES; - public static final TagKey WOODEN_PRESSURE_PLATES = BlockTags.WOODEN_PRESSURE_PLATES; - public static final TagKey WOODEN_SLABS = BlockTags.WOODEN_SLABS; - public static final TagKey WOODEN_STAIRS = BlockTags.WOODEN_STAIRS; - public static final TagKey WOODEN_TRAPDOORS = BlockTags.WOODEN_TRAPDOORS; - public static final TagKey SOUL_FIRE_BASE_BLOCKS = BlockTags.SOUL_FIRE_BASE_BLOCKS; - public static final TagKey SOUL_SPEED_BLOCKS = BlockTags.SOUL_SPEED_BLOCKS; - public static final TagKey BEACON_BASE_BLOCKS = BlockTags.BEACON_BASE_BLOCKS; - public static final TagKey STONE_BRICKS = BlockTags.STONE_BRICKS; -} diff --git a/src/main/java/ru/bclib/api/tag/NamedItemTags.java b/src/main/java/ru/bclib/api/tag/NamedItemTags.java deleted file mode 100644 index 6dabcbca..00000000 --- a/src/main/java/ru/bclib/api/tag/NamedItemTags.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.bclib.api.tag; - -import net.minecraft.tags.ItemTags; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; - - -public class NamedItemTags { - public static final TagKey BUTTONS = ItemTags.BUTTONS; - public static final TagKey DOORS = ItemTags.DOORS; - public static final TagKey FENCES = ItemTags.FENCES; - public static final TagKey LEAVES = ItemTags.LEAVES; - public static final TagKey LOGS = ItemTags.LOGS; - public static final TagKey LOGS_THAT_BURN = ItemTags.LOGS_THAT_BURN; - public static final TagKey PLANKS = ItemTags.PLANKS; - public static final TagKey SAPLINGS = ItemTags.SAPLINGS; - public static final TagKey SIGNS = ItemTags.SIGNS; - public static final TagKey SLABS = ItemTags.SLABS; - public static final TagKey STAIRS = ItemTags.STAIRS; - public static final TagKey TRAPDOORS = ItemTags.TRAPDOORS; - public static final TagKey WOODEN_BUTTONS = ItemTags.WOODEN_BUTTONS; - public static final TagKey WOODEN_DOORS = ItemTags.WOODEN_DOORS; - public static final TagKey WOODEN_FENCES = ItemTags.WOODEN_FENCES; - public static final TagKey WOODEN_PRESSURE_PLATES = ItemTags.WOODEN_PRESSURE_PLATES; - public static final TagKey WOODEN_SLABS = ItemTags.WOODEN_SLABS; - public static final TagKey WOODEN_STAIRS = ItemTags.WOODEN_STAIRS; - public static final TagKey WOODEN_TRAPDOORS = ItemTags.WOODEN_TRAPDOORS; - public static final TagKey BEACON_PAYMENT_ITEMS = ItemTags.BEACON_PAYMENT_ITEMS; - public static final TagKey STONE_BRICKS = ItemTags.STONE_BRICKS; - public static final TagKey STONE_CRAFTING_MATERIALS = ItemTags.STONE_CRAFTING_MATERIALS; - public static final TagKey STONE_TOOL_MATERIALS = ItemTags.STONE_TOOL_MATERIALS; -} diff --git a/src/main/java/ru/bclib/api/tag/NamedMineableTags.java b/src/main/java/ru/bclib/api/tag/NamedMineableTags.java deleted file mode 100644 index 2d36faa8..00000000 --- a/src/main/java/ru/bclib/api/tag/NamedMineableTags.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.bclib.api.tag; - -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.TagKey; -import net.minecraft.world.level.block.Block; - - -public class NamedMineableTags { - public static final TagKey AXE = BlockTags.MINEABLE_WITH_AXE; - public static final TagKey HOE = BlockTags.MINEABLE_WITH_HOE; - public static final TagKey PICKAXE = BlockTags.MINEABLE_WITH_PICKAXE; - public static final TagKey SHEARS = TagAPI.makeBlockTag("fabric", "mineable/shears"); - public static final TagKey SHOVEL = BlockTags.MINEABLE_WITH_SHOVEL; - public static final TagKey SWORD = TagAPI.makeBlockTag("fabric", "mineable/sword"); - public static final TagKey HAMMER = TagAPI.makeCommonBlockTag( "mineable/hammer"); -} diff --git a/src/main/java/ru/bclib/api/tag/NamedToolTags.java b/src/main/java/ru/bclib/api/tag/NamedToolTags.java deleted file mode 100644 index a16d840b..00000000 --- a/src/main/java/ru/bclib/api/tag/NamedToolTags.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.bclib.api.tag; - -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; - - -public class NamedToolTags { - public static final TagKey FABRIC_AXES = TagAPI.makeItemTag("fabric", "axes"); - public static final TagKey FABRIC_HOES = TagAPI.makeItemTag("fabric", "hoes"); - public static final TagKey FABRIC_PICKAXES = TagAPI.makeItemTag("fabric", "pickaxes"); - public static final TagKey FABRIC_SHEARS = TagAPI.makeItemTag("fabric", "shears"); - public static final TagKey FABRIC_SHOVELS = TagAPI.makeItemTag("fabric", "shovels"); - public static final TagKey FABRIC_SWORDS = TagAPI.makeItemTag("fabric", "swords"); -} diff --git a/src/main/java/ru/bclib/api/tag/TagAPI.java b/src/main/java/ru/bclib/api/tag/TagAPI.java deleted file mode 100644 index 2fd4ceac..00000000 --- a/src/main/java/ru/bclib/api/tag/TagAPI.java +++ /dev/null @@ -1,265 +0,0 @@ -package ru.bclib.api.tag; - -import com.google.common.collect.Maps; - -import net.minecraft.core.DefaultedRegistry; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.tags.Tag; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorPreset; - -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.mixin.common.DiggerItemAccessor; - -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -public class TagAPI { - private static final Map> TYPES = Maps.newHashMap(); - - public static TagType.RegistryBacked BLOCKS = registerType(Registry.BLOCK); - public static TagType.RegistryBacked ITEMS = registerType(Registry.ITEM); - public static TagType.Simple BIOMES = registerType(Registry.BIOME_REGISTRY, "tags/worldgen/biome", b->BiomeAPI.getBiomeID(b)); - - private static TagType.RegistryBacked registerType(DefaultedRegistry registry) { - TagType type = new TagType.RegistryBacked<>(registry); - return (TagType.RegistryBacked)TYPES.computeIfAbsent(type.directory, (dir)->type); - } - - public static TagType.Simple registerType(ResourceKey> registry, String directory, Function locationProvider) { - return (TagType.Simple)TYPES.computeIfAbsent(directory, (dir)->new TagType.Simple<>(registry, dir, locationProvider)); - } - - public static TagType.UnTyped registerType(ResourceKey> registry, String directory) { - return (TagType.UnTyped)TYPES.computeIfAbsent(directory, (dir)->new TagType.UnTyped<>(registry, dir)); - } - - /** - * Get or create {@link Block} {@link TagKey} with mod namespace. - * - * @param modID - {@link String} mod namespace (mod id); - * @param name - {@link String} tag name. - * @return {@link Block} {@link TagKey}. - */ - public static TagKey makeBiomeTag(String modID, String name) { - return BIOMES.makeTag(new ResourceLocation(modID, name)); - } - - - /** - * Get or create {@link Block} {@link TagKey} with mod namespace. - * - * @param modID - {@link String} mod namespace (mod id); - * @param name - {@link String} tag name. - * @return {@link Block} {@link TagKey}. - */ - public static TagKey makeBlockTag(String modID, String name) { - return BLOCKS.makeTag(new ResourceLocation(modID, name)); - } - - /** - * Get or create {@link Block} {@link TagKey} with mod namespace. - * - * @param id - {@link String} id for the tag; - * @return {@link Block} {@link TagKey}. - */ - public static TagKey makeBlockTag(ResourceLocation id) { - return BLOCKS.makeTag(id); - } - - /** - * Get or create {@link Item} {@link TagKey} with mod namespace. - * - * @param modID - {@link String} mod namespace (mod id); - * @param name - {@link String} tag name. - * @return {@link Item} {@link TagKey}. - */ - public static TagKey makeItemTag(String modID, String name) { - return ITEMS.makeTag(new ResourceLocation(modID, name)); - } - - /** - * Get or create {@link Item} {@link TagKey} with mod namespace. - * - * @param id - {@link String} id for the tag; - * @return {@link Item} {@link TagKey}. - */ - public static TagKey makeItemTag(ResourceLocation id) { - return ITEMS.makeTag(id); - } - - /** - * Get or create {@link Block} {@link TagKey}. - * - * @param name - {@link String} tag name. - * @return {@link Block} {@link TagKey}. - * @see Fabric Wiki (Tags) - */ - public static TagKey makeCommonBlockTag(String name) { - return BLOCKS.makeCommonTag(name); - } - - /** - * Get or create {@link Item} {@link TagKey}. - * - * @param name - {@link String} tag name. - * @return {@link Item} {@link TagKey}. - * @see Fabric Wiki (Tags) - */ - public static TagKey makeCommonItemTag(String name) { - return ITEMS.makeCommonTag(name); - } - - public static TagKey makeCommonBiomeTag(String name) { - return BIOMES.makeCommonTag(name); - } - - /** - * Initializes basic tags. Should be called only in BCLib main class. - */ - public static void init() { - addBlockTag(CommonBlockTags.BOOKSHELVES, Blocks.BOOKSHELF); - addBlockTag(CommonBlockTags.CHEST, Blocks.CHEST); - addItemTag(CommonItemTags.CHEST, Items.CHEST); - addItemTag(CommonItemTags.IRON_INGOTS, Items.IRON_INGOT); - addItemTag(CommonItemTags.FURNACES, Blocks.FURNACE); - } - - /** - * Adds multiple Tags to one Biome. - * @param tagIDs array of {@link TagKey} tag IDs. - * @param biome The {@link Biome} to add tag. - */ - @SafeVarargs - @Deprecated(forRemoval = true) - public static void addBiomeTags(Biome biome, TagKey... tagIDs) { - BIOMES.add(biome, tagIDs); - } - - /** - * Adds one Tag to multiple Biomes. - * @param tagID {@link TagKey} tag ID. - * @param biomes array of {@link Biome} to add into tag. - */ - public static void addBiomeTag(TagKey tagID, Biome... biomes) { - BIOMES.add(tagID, biomes); - } - - - /** - * Adds multiple Tags to one Block. - * @param tagIDs array of {@link TagKey} tag IDs. - * @param block The {@link Block} to add tag. - */ - @SafeVarargs - @Deprecated(forRemoval = true) - public static void addBlockTags(Block block, TagKey... tagIDs) { - BLOCKS.add(block, tagIDs); - } - - - /** - * Adds one Tag to multiple Blocks. - * @param tagID {@link TagKey} tag ID. - * @param blocks array of {@link Block} to add into tag. - */ - public static void addBlockTag(TagKey tagID, Block... blocks) { - BLOCKS.add(tagID, blocks); - } - - /** - * Adds multiple Tags to one Item. - * @param tagIDs array of {@link TagKey} tag IDs. - * @param item The {@link Item} to add tag. - */ - @Deprecated(forRemoval = true) - @SafeVarargs - public static void addItemTags(ItemLike item, TagKey... tagIDs) { - ITEMS.add(item.asItem(), tagIDs); - } - - /** - * Adds one Tag to multiple Items. - * @param tagID {@link TagKey} tag ID. - * @param items array of {@link ItemLike} to add into tag. - */ - public static void addItemTag(TagKey tagID, ItemLike... items) { - for(ItemLike i : items){ - ITEMS.add(i.asItem(), tagID); - } - } - - /** - * Adds one Tag to multiple Items. - * @param tagID {@link TagKey} tag ID. - * @param items array of {@link ItemLike} to add into tag. - */ - public static void addItemTag(TagKey tagID, Item... items) { - ITEMS.add(tagID, items); - } - - - - /** - * Automatically called in {@link net.minecraft.tags.TagLoader#loadAndBuild(ResourceManager)}. - *

- * In most cases there is no need to call this Method manually. - * - * @param directory The name of the Tag-directory. Should be either "tags/blocks" or - * "tags/items". - * @param tagsMap The map that will hold the registered Tags - * @return The {@code tagsMap} Parameter. - */ - public static Map apply(String directory, Map tagsMap) { - - TagType type = TYPES.get(directory); - if (type!=null){ - type.apply(tagsMap); - } - -// final BiConsumer> consumer; -// consumer = (id, ids) -> apply(tagsMap.computeIfAbsent(id, key -> Tag.Builder.tag()), ids); -// -// if ("tags/blocks".equals(directory)) { -// TAGS_BLOCK.forEach(consumer); -// } -// else if ("tags/items".equals(directory)) { -// TAGS_ITEM.forEach(consumer); -// } -// else if ("tags/worldgen/biome".equals(directory)) { -// TAGS_BIOME.forEach(consumer); -// } - return tagsMap; - } - - /** - * Adds all {@code ids} to the {@code builder}. - * - * @param builder - * @param ids - * @return The Builder passed as {@code builder}. - */ - public static Tag.Builder apply(Tag.Builder builder, Set ids) { - ids.forEach(value -> builder.addElement(value, "BCLib Code")); - return builder; - } - - - public static boolean isToolWithMineableTag(ItemStack stack, TagKey tag){ - if (stack.getItem() instanceof DiggerItemAccessor dig){ - return dig.bclib_getBlockTag().equals(tag); - } - return false; - } -} diff --git a/src/main/java/ru/bclib/blockentities/BaseBarrelBlockEntity.java b/src/main/java/ru/bclib/blockentities/BaseBarrelBlockEntity.java deleted file mode 100644 index cad3472f..00000000 --- a/src/main/java/ru/bclib/blockentities/BaseBarrelBlockEntity.java +++ /dev/null @@ -1,150 +0,0 @@ -package ru.bclib.blockentities; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.NonNullList; -import net.minecraft.core.Vec3i; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; - -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.entity.ChestBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.blocks.BaseBarrelBlock; -import ru.bclib.registry.BaseBlockEntities; - -public class BaseBarrelBlockEntity extends RandomizableContainerBlockEntity { - private NonNullList inventory; - private int viewerCount; - - private BaseBarrelBlockEntity(BlockEntityType type, BlockPos blockPos, BlockState blockState) { - super(type, blockPos, blockState); - this.inventory = NonNullList.withSize(27, ItemStack.EMPTY); - } - - public BaseBarrelBlockEntity(BlockPos blockPos, BlockState blockState) { - this(BaseBlockEntities.BARREL, blockPos, blockState); - } - - @Override - public void saveAdditional(CompoundTag tag) { - super.saveAdditional(tag); - if (!this.trySaveLootTable(tag)) { - ContainerHelper.saveAllItems(tag, this.inventory); - } - - //return tag; - } - - public void load(CompoundTag tag) { - super.load(tag); - this.inventory = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); - if (!this.tryLoadLootTable(tag)) { - ContainerHelper.loadAllItems(tag, this.inventory); - } - } - - public int getContainerSize() { - return 27; - } - - protected NonNullList getItems() { - return this.inventory; - } - - protected void setItems(NonNullList list) { - this.inventory = list; - } - - protected Component getDefaultName() { - return Component.translatable("container.barrel"); - } - - protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) { - return ChestMenu.threeRows(syncId, playerInventory, this); - } - - public void startOpen(Player player) { - if (!player.isSpectator()) { - if (viewerCount < 0) { - viewerCount = 0; - } - - ++viewerCount; - BlockState blockState = this.getBlockState(); - if (!blockState.getValue(BarrelBlock.OPEN)) { - playSound(blockState, SoundEvents.BARREL_OPEN); - setOpen(blockState, true); - } - - if (level != null) { - scheduleUpdate(); - } - } - } - - private void scheduleUpdate() { - level.scheduleTick(getBlockPos(), getBlockState().getBlock(), 5); - } - - public void tick() { - if (level != null) { - viewerCount = ChestBlockEntity.getOpenCount(level, worldPosition); - if (viewerCount > 0) { - scheduleUpdate(); - } - else { - BlockState blockState = getBlockState(); - if (!(blockState.getBlock() instanceof BaseBarrelBlock)) { - setRemoved(); - return; - } - if (blockState.getValue(BarrelBlock.OPEN)) { - playSound(blockState, SoundEvents.BARREL_CLOSE); - setOpen(blockState, false); - } - } - } - } - - public void stopOpen(Player player) { - if (!player.isSpectator()) { - --this.viewerCount; - } - } - - private void setOpen(BlockState state, boolean open) { - if (level != null) { - level.setBlock(this.getBlockPos(), state.setValue(BarrelBlock.OPEN, open), 3); - } - } - - private void playSound(BlockState blockState, SoundEvent soundEvent) { - if (level != null) { - Vec3i vec3i = blockState.getValue(BarrelBlock.FACING).getNormal(); - double d = (double) this.worldPosition.getX() + 0.5D + (double) vec3i.getX() / 2.0D; - double e = (double) this.worldPosition.getY() + 0.5D + (double) vec3i.getY() / 2.0D; - double f = (double) this.worldPosition.getZ() + 0.5D + (double) vec3i.getZ() / 2.0D; - level.playSound( - null, - d, - e, - f, - soundEvent, - SoundSource.BLOCKS, - 0.5F, - this.level.random.nextFloat() * 0.1F + 0.9F - ); - } - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blockentities/BaseChestBlockEntity.java b/src/main/java/ru/bclib/blockentities/BaseChestBlockEntity.java deleted file mode 100644 index 97581739..00000000 --- a/src/main/java/ru/bclib/blockentities/BaseChestBlockEntity.java +++ /dev/null @@ -1,12 +0,0 @@ -package ru.bclib.blockentities; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.entity.ChestBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.registry.BaseBlockEntities; - -public class BaseChestBlockEntity extends ChestBlockEntity { - public BaseChestBlockEntity(BlockPos blockPos, BlockState blockState) { - super(BaseBlockEntities.CHEST, blockPos, blockState); - } -} diff --git a/src/main/java/ru/bclib/blockentities/BaseFurnaceBlockEntity.java b/src/main/java/ru/bclib/blockentities/BaseFurnaceBlockEntity.java deleted file mode 100644 index 935f91eb..00000000 --- a/src/main/java/ru/bclib/blockentities/BaseFurnaceBlockEntity.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.bclib.blockentities; - -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; - -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.FurnaceMenu; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.registry.BaseBlockEntities; - -public class BaseFurnaceBlockEntity extends AbstractFurnaceBlockEntity { - public BaseFurnaceBlockEntity(BlockPos blockPos, BlockState blockState) { - super(BaseBlockEntities.FURNACE, blockPos, blockState, RecipeType.SMELTING); - } - - protected Component getDefaultName() { - return Component.translatable("container.furnace"); - } - - protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) { - return new FurnaceMenu(syncId, playerInventory, this, this.dataAccess); - } -} diff --git a/src/main/java/ru/bclib/blockentities/BaseSignBlockEntity.java b/src/main/java/ru/bclib/blockentities/BaseSignBlockEntity.java deleted file mode 100644 index a2ebfb31..00000000 --- a/src/main/java/ru/bclib/blockentities/BaseSignBlockEntity.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.bclib.blockentities; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.entity.SignBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.registry.BaseBlockEntities; - -public class BaseSignBlockEntity extends SignBlockEntity { - public BaseSignBlockEntity(BlockPos blockPos, BlockState blockState) { - super(blockPos, blockState); - } - - @Override - public BlockEntityType getType() { - return BaseBlockEntities.SIGN; - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blockentities/DynamicBlockEntityType.java b/src/main/java/ru/bclib/blockentities/DynamicBlockEntityType.java deleted file mode 100644 index ad656251..00000000 --- a/src/main/java/ru/bclib/blockentities/DynamicBlockEntityType.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.bclib.blockentities; - -import com.google.common.collect.Sets; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.Set; - -public class DynamicBlockEntityType extends BlockEntityType { - - private final Set validBlocks = Sets.newHashSet(); - private final BlockEntitySupplier factory; - - public DynamicBlockEntityType(BlockEntitySupplier supplier) { - super(null, Collections.emptySet(), null); - this.factory = supplier; - } - - @Override - @Nullable - public T create(BlockPos blockPos, BlockState blockState) { - return factory.create(blockPos, blockState); - } - - @Override - public boolean isValid(BlockState blockState) { - return validBlocks.contains(blockState.getBlock()); - } - - public void registerBlock(Block block) { - validBlocks.add(block); - } - - @FunctionalInterface - public interface BlockEntitySupplier { - T create(BlockPos blockPos, BlockState blockState); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseAnvilBlock.java b/src/main/java/ru/bclib/blocks/BaseAnvilBlock.java deleted file mode 100644 index 678170dc..00000000 --- a/src/main/java/ru/bclib/blocks/BaseAnvilBlock.java +++ /dev/null @@ -1,135 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.PickaxeItem; -import net.minecraft.world.level.block.AnvilBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -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.interfaces.BlockModelProvider; -import ru.bclib.interfaces.CustomItemProvider; -import ru.bclib.items.BaseAnvilItem; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Random;import net.minecraft.util.RandomSource; - -public abstract class BaseAnvilBlock extends AnvilBlock implements BlockModelProvider, CustomItemProvider { - public static final IntegerProperty DESTRUCTION = BlockProperties.DESTRUCTION; - public IntegerProperty durability; - - public BaseAnvilBlock(MaterialColor color) { - this(FabricBlockSettings.copyOf(Blocks.ANVIL).color(color)); - } - - public BaseAnvilBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - super.createBlockStateDefinition(builder); - if (getMaxDurability() != 3) { - durability = IntegerProperty.create("durability", 0, getMaxDurability()); - } - else { - durability = BlockProperties.DEFAULT_ANVIL_DURABILITY; - } - builder.add(DESTRUCTION, durability); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - int destruction = blockState.getValue(DESTRUCTION); - String name = blockId.getPath(); - Map textures = Maps.newHashMap(); - textures.put("%modid%", blockId.getNamespace()); - textures.put("%anvil%", name); - textures.put("%top%", name + "_top_" + destruction); - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_ANVIL, textures); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - int destruction = blockState.getValue(DESTRUCTION); - String modId = stateId.getNamespace(); - String modelId = "block/" + stateId.getPath() + "_top_" + destruction; - ResourceLocation modelLocation = new ResourceLocation(modId, modelId); - registerBlockModel(stateId, modelLocation, blockState, modelCache); - return ModelsHelper.createFacingModel(modelLocation, blockState.getValue(FACING), false, false); - } - - @Override - public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { - return new BaseAnvilItem(this, settings); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - int destruction = state.getValue(DESTRUCTION); - int durability = state.getValue(getDurabilityProp()); - int value = destruction * getMaxDurability() + durability; - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && tool.getItem() instanceof PickaxeItem) { - ItemStack itemStack = new ItemStack(this); - itemStack.getOrCreateTag().putInt(BaseAnvilItem.DESTRUCTION, value); - return Lists.newArrayList(itemStack); - } - return Collections.emptyList(); - } - - public IntegerProperty getDurabilityProp() { - return durability; - } - - public int getMaxDurability() { - return 3; - } - - public BlockState damageAnvilUse(BlockState state, RandomSource random) { - IntegerProperty durability = getDurabilityProp(); - int value = state.getValue(durability); - if (value < getMaxDurability() && random.nextInt(8) == 0) { - return state.setValue(durability, value + 1); - } - value = state.getValue(DESTRUCTION); - return value < 2 ? state.setValue(DESTRUCTION, value + 1).setValue(durability, 0) : null; - } - - public BlockState damageAnvilFall(BlockState state) { - int destruction = state.getValue(DESTRUCTION); - return destruction < 2 ? state.setValue(DESTRUCTION, destruction + 1) : null; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseAttachedBlock.java b/src/main/java/ru/bclib/blocks/BaseAttachedBlock.java deleted file mode 100644 index a0e2b1af..00000000 --- a/src/main/java/ru/bclib/blocks/BaseAttachedBlock.java +++ /dev/null @@ -1,76 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.DirectionProperty; -import ru.bclib.util.BlocksHelper; - -@SuppressWarnings("deprecation") -public abstract class BaseAttachedBlock extends BaseBlockNotFull { - public static final DirectionProperty FACING = BlockStateProperties.FACING; - - public BaseAttachedBlock(Properties settings) { - super(settings); - registerDefaultState(defaultBlockState().setValue(FACING, Direction.UP)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(FACING); - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext ctx) { - BlockState blockState = defaultBlockState(); - LevelReader worldView = ctx.getLevel(); - BlockPos blockPos = ctx.getClickedPos(); - Direction[] directions = ctx.getNearestLookingDirections(); - for (Direction direction : directions) { - Direction direction2 = direction.getOpposite(); - blockState = blockState.setValue(FACING, direction2); - if (blockState.canSurvive(worldView, blockPos)) { - return blockState; - } - } - return null; - } - - @Override - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - Direction direction = state.getValue(FACING); - BlockPos blockPos = pos.relative(direction.getOpposite()); - return canSupportCenter(world, blockPos, direction) || world.getBlockState(blockPos).is(BlockTags.LEAVES); - } - - @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 BlockState rotate(BlockState state, Rotation rotation) { - return BlocksHelper.rotateHorizontal(state, rotation, FACING); - } - - @Override - public BlockState mirror(BlockState state, Mirror mirror) { - return BlocksHelper.mirrorHorizontal(state, mirror, FACING); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseBarkBlock.java b/src/main/java/ru/bclib/blocks/BaseBarkBlock.java deleted file mode 100644 index 7adca093..00000000 --- a/src/main/java/ru/bclib/blocks/BaseBarkBlock.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.client.models.BasePatterns; -import ru.bclib.client.models.PatternsHelper; - -import java.util.Optional; - -public class BaseBarkBlock extends BaseRotatedPillarBlock { - public BaseBarkBlock(Properties settings) { - super(settings); - } - - @Override - protected Optional createBlockPattern(ResourceLocation blockId) { - blockId = Registry.BLOCK.getKey(this); - return PatternsHelper.createJson(BasePatterns.BLOCK_BASE, replacePath(blockId)); - } - - private ResourceLocation replacePath(ResourceLocation blockId) { - String newPath = blockId.getPath().replace("_bark", "_log_side"); - return new ResourceLocation(blockId.getNamespace(), newPath); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseBarrelBlock.java b/src/main/java/ru/bclib/blocks/BaseBarrelBlock.java deleted file mode 100644 index e22326f2..00000000 --- a/src/main/java/ru/bclib/blocks/BaseBarrelBlock.java +++ /dev/null @@ -1,152 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.stats.Stats; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.monster.piglin.PiglinAi; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.RenderShape; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.phys.BlockHitResult; -import org.jetbrains.annotations.Nullable; -import ru.bclib.blockentities.BaseBarrelBlockEntity; -import ru.bclib.client.models.BasePatterns; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; -import ru.bclib.interfaces.BlockModelProvider; -import ru.bclib.registry.BaseBlockEntities; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Random;import net.minecraft.util.RandomSource; - -public class BaseBarrelBlock extends BarrelBlock implements BlockModelProvider { - public BaseBarrelBlock(Block source) { - this(FabricBlockSettings.copyOf(source).noOcclusion()); - } - - public BaseBarrelBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - return BaseBlockEntities.BARREL.create(blockPos, blockState); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - List drop = super.getDrops(state, builder); - drop.add(new ItemStack(this.asItem())); - return drop; - } - - @Override - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - if (world.isClientSide) { - return InteractionResult.SUCCESS; - } - else { - BlockEntity blockEntity = world.getBlockEntity(pos); - if (blockEntity instanceof BaseBarrelBlockEntity) { - player.openMenu((BaseBarrelBlockEntity) blockEntity); - player.awardStat(Stats.OPEN_BARREL); - PiglinAi.angerNearbyPiglins(player, true); - } - - return InteractionResult.CONSUME; - } - } - - @Override - public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - BlockEntity blockEntity = world.getBlockEntity(pos); - if (blockEntity instanceof BaseBarrelBlockEntity) { - ((BaseBarrelBlockEntity) blockEntity).tick(); - } - } - - @Override - public RenderShape getRenderShape(BlockState state) { - return RenderShape.MODEL; - } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { - if (itemStack.hasCustomHoverName()) { - BlockEntity blockEntity = world.getBlockEntity(pos); - if (blockEntity instanceof BaseBarrelBlockEntity) { - ((BaseBarrelBlockEntity) blockEntity).setCustomName(itemStack.getHoverName()); - } - } - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - Optional pattern; - if (blockState.getValue(OPEN)) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BARREL_OPEN, blockId); - } - else { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BOTTOM_TOP, blockId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - String open = blockState.getValue(OPEN) ? "_open" : ""; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + open); - registerBlockModel(stateId, modelId, blockState, modelCache); - Direction facing = blockState.getValue(FACING); - BlockModelRotation rotation = BlockModelRotation.X0_Y0; - switch (facing) { - case NORTH: - rotation = BlockModelRotation.X90_Y0; - break; - case EAST: - rotation = BlockModelRotation.X90_Y90; - break; - case SOUTH: - rotation = BlockModelRotation.X90_Y180; - break; - case WEST: - rotation = BlockModelRotation.X90_Y270; - break; - case DOWN: - rotation = BlockModelRotation.X180_Y0; - break; - default: - break; - } - return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseBlock.java b/src/main/java/ru/bclib/blocks/BaseBlock.java deleted file mode 100644 index a8776744..00000000 --- a/src/main/java/ru/bclib/blocks/BaseBlock.java +++ /dev/null @@ -1,73 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.level.storage.loot.LootContext; -import ru.bclib.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -/** - * Base class for a default Block. - *

- * This Block-Type will: - *

    - *
  • Drop itself
  • - *
  • Automatically create an Item-Model from the Block-Model
  • - *
- */ -public class BaseBlock extends Block implements BlockModelProvider { - /** - * Creates a new Block with the passed properties - * - * @param settings The properties of the Block. - */ - public BaseBlock(Properties settings) { - super(settings); - } - - /** - * {@inheritDoc} - *

- * This implementation will drop the Block itself - */ - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - /** - * {@inheritDoc} - *

- * This implementation will load the Block-Model and return it as the Item-Model - */ - @Override - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - /** - * This method is used internally. - *

- * It is called from Block-Contructors, to allow the augmentation of the blocks - * preset properties. - *

- * For example in {@link BaseLeavesBlock#BaseLeavesBlock(Block, MaterialColor, Consumer)} - * - * @param customizeProperties A {@link Consumer} to call with the preset properties - * @param settings The properties as created by the Block - * @return The reconfigured {@code settings} - */ - static FabricBlockSettings acceptAndReturn(Consumer customizeProperties, FabricBlockSettings settings) { - customizeProperties.accept(settings); - return settings; - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/BaseBlockNotFull.java b/src/main/java/ru/bclib/blocks/BaseBlockNotFull.java deleted file mode 100644 index d483ea4f..00000000 --- a/src/main/java/ru/bclib/blocks/BaseBlockNotFull.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.block.state.BlockState; - -public class BaseBlockNotFull extends BaseBlock { - public BaseBlockNotFull(Properties settings) { - super(settings); - } - - public boolean canSuffocate(BlockState state, BlockGetter view, BlockPos pos) { - return false; - } - - public boolean isSimpleFullBlock(BlockState state, BlockGetter view, BlockPos pos) { - return false; - } - - public boolean allowsSpawning(BlockState state, BlockGetter view, BlockPos pos, EntityType type) { - return false; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseBlockWithEntity.java b/src/main/java/ru/bclib/blocks/BaseBlockWithEntity.java deleted file mode 100644 index 26b9cf22..00000000 --- a/src/main/java/ru/bclib/blocks/BaseBlockWithEntity.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.BaseEntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; - -import java.util.Collections; -import java.util.List; - -public class BaseBlockWithEntity extends BaseEntityBlock { - public BaseBlockWithEntity(Properties settings) { - super(settings); - } - - @Override - public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - return null; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseBookshelfBlock.java b/src/main/java/ru/bclib/blocks/BaseBookshelfBlock.java deleted file mode 100644 index 7d3d3a78..00000000 --- a/src/main/java/ru/bclib/blocks/BaseBookshelfBlock.java +++ /dev/null @@ -1,58 +0,0 @@ -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.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.enchantment.EnchantmentHelper; -import net.minecraft.world.item.enchantment.Enchantments; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import org.jetbrains.annotations.Nullable; -import ru.bclib.client.models.BasePatterns; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -public class BaseBookshelfBlock extends BaseBlock { - public BaseBookshelfBlock(Block source) { - this(FabricBlockSettings.copyOf(source)); - } - - public BaseBookshelfBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null) { - int silk = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool); - if (silk > 0) { - return Collections.singletonList(new ItemStack(this)); - } - } - return Collections.singletonList(new ItemStack(Items.BOOK, 3)); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BOOKSHELF, replacePath(blockId)); - return ModelsHelper.fromPattern(pattern); - } - - private ResourceLocation replacePath(ResourceLocation blockId) { - String newPath = blockId.getPath().replace("_bookshelf", ""); - return new ResourceLocation(blockId.getNamespace(), newPath); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseButtonBlock.java b/src/main/java/ru/bclib/blocks/BaseButtonBlock.java deleted file mode 100644 index e60f2ba0..00000000 --- a/src/main/java/ru/bclib/blocks/BaseButtonBlock.java +++ /dev/null @@ -1,102 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ButtonBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.AttachFace; -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.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public abstract class BaseButtonBlock extends ButtonBlock implements BlockModelProvider { - private final Block parent; - - protected BaseButtonBlock(Block parent, Properties properties, boolean sensitive) { - super(sensitive, properties); - this.parent = parent; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_BUTTON, parentId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern = blockState.getValue(POWERED) ? PatternsHelper.createJson( - BasePatterns.BLOCK_BUTTON_PRESSED, - parentId - ) : PatternsHelper.createJson(BasePatterns.BLOCK_BUTTON, parentId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - String powered = blockState.getValue(POWERED) ? "_powered" : ""; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + powered); - registerBlockModel(stateId, modelId, blockState, modelCache); - AttachFace face = blockState.getValue(FACE); - boolean isCeiling = face == AttachFace.CEILING; - int x = 0, y = 0; - switch (face) { - case CEILING: - x = 180; - break; - case WALL: - x = 90; - break; - default: - break; - } - switch (blockState.getValue(FACING)) { - case NORTH: - if (isCeiling) { - y = 180; - } - break; - case EAST: - y = isCeiling ? 270 : 90; - break; - case SOUTH: - if (!isCeiling) { - y = 180; - } - break; - case WEST: - y = isCeiling ? 90 : 270; - break; - default: - break; - } - BlockModelRotation rotation = BlockModelRotation.by(x, y); - return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), face == AttachFace.WALL); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseChainBlock.java b/src/main/java/ru/bclib/blocks/BaseChainBlock.java deleted file mode 100644 index 4af5392b..00000000 --- a/src/main/java/ru/bclib/blocks/BaseChainBlock.java +++ /dev/null @@ -1,71 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.ChainBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.MaterialColor; -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.Map; -import java.util.Optional; - -public class BaseChainBlock extends ChainBlock implements BlockModelProvider, RenderLayerProvider { - public BaseChainBlock(MaterialColor color) { - this(FabricBlockSettings.copyOf(Blocks.CHAIN).color(color)); - } - - public BaseChainBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return ModelsHelper.createItemModel(blockId); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CHAIN, blockId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - Direction.Axis axis = blockState.getValue(AXIS); - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createRotatedModel(modelId, axis); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseChestBlock.java b/src/main/java/ru/bclib/blocks/BaseChestBlock.java deleted file mode 100644 index 6ebc000d..00000000 --- a/src/main/java/ru/bclib/blocks/BaseChestBlock.java +++ /dev/null @@ -1,60 +0,0 @@ -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.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -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.interfaces.BlockModelProvider; -import ru.bclib.registry.BaseBlockEntities; - -import java.util.List; -import java.util.Optional; - -public class BaseChestBlock extends ChestBlock implements BlockModelProvider { - private final Block parent; - - public BaseChestBlock(Block source) { - super(FabricBlockSettings.copyOf(source).noOcclusion(), () -> BaseBlockEntities.CHEST); - this.parent = source; - } - - @Override - public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - return BaseBlockEntities.CHEST.create(blockPos, blockState); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - List drop = super.getDrops(state, builder); - drop.add(new ItemStack(this.asItem())); - return drop; - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_CHEST, blockId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - return ModelsHelper.createBlockEmpty(parentId); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseComposterBlock.java b/src/main/java/ru/bclib/blocks/BaseComposterBlock.java deleted file mode 100644 index 8a977e30..00000000 --- a/src/main/java/ru/bclib/blocks/BaseComposterBlock.java +++ /dev/null @@ -1,73 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ComposterBlock; -import net.minecraft.world.level.block.state.BlockState; -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.ModelsHelper.MultiPartBuilder; -import ru.bclib.client.models.PatternsHelper; -import ru.bclib.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseComposterBlock extends ComposterBlock implements BlockModelProvider { - public BaseComposterBlock(Block source) { - super(FabricBlockSettings.copyOf(source)); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this.asItem())); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_COMPOSTER, blockId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - - MultiPartBuilder builder = MultiPartBuilder.create(stateDefinition); - LEVEL.getPossibleValues().forEach(level -> { - if (level > 0) { - ResourceLocation contentId; - if (level > 7) { - contentId = new ResourceLocation("block/composter_contents_ready"); - } - else { - contentId = new ResourceLocation("block/composter_contents" + level); - } - builder.part(contentId).setCondition(state -> state.getValue(LEVEL).equals(level)).add(); - } - }); - builder.part(modelId).add(); - - return builder.build(); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseCraftingTableBlock.java b/src/main/java/ru/bclib/blocks/BaseCraftingTableBlock.java deleted file mode 100644 index a161d979..00000000 --- a/src/main/java/ru/bclib/blocks/BaseCraftingTableBlock.java +++ /dev/null @@ -1,66 +0,0 @@ -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.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.CraftingTableBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -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.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Optional; - -public class BaseCraftingTableBlock extends CraftingTableBlock implements BlockModelProvider { - public BaseCraftingTableBlock(Block source) { - this(FabricBlockSettings.copyOf(source)); - } - - public BaseCraftingTableBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this.asItem())); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - String blockName = blockId.getPath(); - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_SIDED, new HashMap() { - private static final long serialVersionUID = 1L; - - { - put("%modid%", blockId.getNamespace()); - put("%particle%", blockName + "_front"); - put("%down%", blockName + "_bottom"); - put("%up%", blockName + "_top"); - put("%north%", blockName + "_front"); - put("%south%", blockName + "_side"); - put("%west%", blockName + "_front"); - put("%east%", blockName + "_side"); - } - }); - return ModelsHelper.fromPattern(pattern); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseCropBlock.java b/src/main/java/ru/bclib/blocks/BaseCropBlock.java deleted file mode 100644 index 4e99cb1d..00000000 --- a/src/main/java/ru/bclib/blocks/BaseCropBlock.java +++ /dev/null @@ -1,122 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Mth; -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.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; -import ru.bclib.util.BlocksHelper; -import ru.bclib.util.MHelper; - -import java.util.Collections; -import java.util.List; -import java.util.Random;import net.minecraft.util.RandomSource; - -public class BaseCropBlock extends BasePlantBlock { - public static final IntegerProperty AGE = IntegerProperty.create("age", 0, 3); - private static final VoxelShape SHAPE = Block.box(2, 0, 2, 14, 14, 14); - - private final Block[] terrain; - private final Item drop; - - public BaseCropBlock(Item drop, Block... terrain) { - this( - FabricBlockSettings.of(Material.PLANT) - .sound(SoundType.GRASS) - .randomTicks() - .noCollission() - .offsetType(BlockBehaviour.OffsetType.NONE), - drop, terrain - ); - } - - public BaseCropBlock(BlockBehaviour.Properties properties, Item drop, Block... terrain) { - super(properties.offsetType(BlockBehaviour.OffsetType.NONE)); - this.drop = drop; - this.terrain = terrain; - this.registerDefaultState(defaultBlockState().setValue(AGE, 0)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(AGE); - } - - @Override - protected boolean isTerrain(BlockState state) { - for (Block block : terrain) { - if (state.is(block)) { - return true; - } - } - return false; - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - if (state.getValue(AGE) < 3) { - return Collections.singletonList(new ItemStack(this)); - } - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && tool.isCorrectToolForDrops(state)) { - int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); - if (enchantment > 0) { - int countSeeds = MHelper.randRange(Mth.clamp(1 + enchantment, 1, 3), 3, MHelper.RANDOM_SOURCE); - int countDrops = MHelper.randRange(Mth.clamp(1 + enchantment, 1, 2), 2, MHelper.RANDOM_SOURCE); - return Lists.newArrayList(new ItemStack(this, countSeeds), new ItemStack(drop, countDrops)); - } - } - int countSeeds = MHelper.randRange(1, 3, MHelper.RANDOM_SOURCE); - int countDrops = MHelper.randRange(1, 2, MHelper.RANDOM_SOURCE); - return Lists.newArrayList(new ItemStack(this, countSeeds), new ItemStack(drop, countDrops)); - } - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - int age = state.getValue(AGE); - if (age < 3) { - BlocksHelper.setWithUpdate(level, pos, state.setValue(AGE, age + 1)); - } - } - - @Override - public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { - return state.getValue(AGE) < 3; - } - - @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { - return state.getValue(AGE) < 3; - } - - @Override - @SuppressWarnings("deprecation") - public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - super.tick(state, world, pos, random); - if (isBonemealSuccess(world, random, pos, state) && random.nextInt(8) == 0) { - performBonemeal(world, random, pos, state); - } - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPE; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseDoorBlock.java b/src/main/java/ru/bclib/blocks/BaseDoorBlock.java deleted file mode 100644 index 96c7be13..00000000 --- a/src/main/java/ru/bclib/blocks/BaseDoorBlock.java +++ /dev/null @@ -1,183 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.util.StringRepresentable; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.DoorBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.DoorHingeSide; -import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; -import net.minecraft.world.level.storage.loot.LootContext; -import org.jetbrains.annotations.Nullable; -import ru.bclib.api.tag.NamedBlockTags; -import ru.bclib.api.tag.NamedItemTags; - -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 ru.bclib.interfaces.TagProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseDoorBlock extends DoorBlock implements RenderLayerProvider, BlockModelProvider, TagProvider { - public BaseDoorBlock(Block source) { - this(FabricBlockSettings.copyOf(source).strength(3F, 3F).noOcclusion()); - } - - public BaseDoorBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - if (state.getValue(HALF) == DoubleBlockHalf.LOWER) - return Collections.singletonList(new ItemStack(this.asItem())); - else return Collections.emptyList(); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - DoorType doorType = getDoorType(blockState); - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_BOTTOM, resourceLocation); - switch (doorType) { - case TOP_HINGE: - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_TOP_HINGE, resourceLocation); - break; - case BOTTOM_HINGE: - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_BOTTOM_HINGE, resourceLocation); - break; - case TOP: - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_DOOR_TOP, resourceLocation); - break; - default: - break; - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - Direction facing = blockState.getValue(FACING); - DoorType doorType = getDoorType(blockState); - boolean open = blockState.getValue(OPEN); - boolean hinge = doorType.isHinge(); - BlockModelRotation rotation = BlockModelRotation.X0_Y0; - switch (facing) { - case EAST: - if (hinge && open) { - rotation = BlockModelRotation.X0_Y90; - } - else if (open) { - rotation = BlockModelRotation.X0_Y270; - } - break; - case SOUTH: - if (!hinge && !open || hinge && !open) { - rotation = BlockModelRotation.X0_Y90; - } - else if (hinge) { - rotation = BlockModelRotation.X0_Y180; - } - break; - case WEST: - if (!hinge && !open || hinge && !open) { - rotation = BlockModelRotation.X0_Y180; - } - else if (hinge) { - rotation = BlockModelRotation.X0_Y270; - } - else { - rotation = BlockModelRotation.X0_Y90; - } - break; - case NORTH: - default: - if (!hinge && !open || hinge && !open) { - rotation = BlockModelRotation.X0_Y270; - } - else if (!hinge) { - rotation = BlockModelRotation.X0_Y180; - } - break; - } - ResourceLocation modelId = new ResourceLocation( - stateId.getNamespace(), - "block/" + stateId.getPath() + "_" + doorType - ); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); - } - - protected DoorType getDoorType(BlockState blockState) { - boolean isHinge = isHinge(blockState.getValue(HINGE), blockState.getValue(OPEN)); - switch (blockState.getValue(HALF)) { - case UPPER: { - return isHinge ? DoorType.TOP_HINGE : DoorType.TOP; - } - case LOWER: { - return isHinge ? DoorType.BOTTOM_HINGE : DoorType.BOTTOM; - } - } - return DoorType.BOTTOM; - } - - private boolean isHinge(DoorHingeSide hingeSide, boolean open) { - boolean isHinge = hingeSide == DoorHingeSide.RIGHT; - return isHinge && !open || !isHinge && open; - } - - @Override - public void addTags(List> blockTags, List> itemTags) { - blockTags.add(NamedBlockTags.DOORS); - itemTags.add(NamedItemTags.DOORS); - } - - protected enum DoorType implements StringRepresentable { - BOTTOM_HINGE("bottom_hinge"), TOP_HINGE("top_hinge"), BOTTOM("bottom"), TOP("top"); - - private final String name; - - DoorType(String name) { - this.name = name; - } - - public boolean isHinge() { - return this == BOTTOM_HINGE || this == TOP_HINGE; - } - - @Override - public String toString() { - return getSerializedName(); - } - - @Override - public String getSerializedName() { - return name; - } - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseDoublePlantBlock.java b/src/main/java/ru/bclib/blocks/BaseDoublePlantBlock.java deleted file mode 100644 index 314f85a7..00000000 --- a/src/main/java/ru/bclib/blocks/BaseDoublePlantBlock.java +++ /dev/null @@ -1,161 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.item.ItemEntity; -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.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.BonemealableBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.items.tool.BaseShearsItem; -import ru.bclib.util.BlocksHelper; - -import java.util.List; -import java.util.Random;import net.minecraft.util.RandomSource; - -public abstract class BaseDoublePlantBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock { - private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12); - public static final IntegerProperty ROTATION = BlockProperties.ROTATION; - public static final BooleanProperty TOP = BooleanProperty.create("top"); - - public BaseDoublePlantBlock() { - this( - FabricBlockSettings.of(Material.PLANT) - .sound(SoundType.GRASS) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ) - ); - } - - public BaseDoublePlantBlock(int light) { - this( - FabricBlockSettings.of(Material.PLANT) - .sound(SoundType.GRASS) - .lightLevel((state) -> state.getValue(TOP) ? light : 0) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ) - ); - } - - public BaseDoublePlantBlock(BlockBehaviour.Properties properties) { - super(properties); - this.registerDefaultState(this.stateDefinition.any().setValue(TOP, false)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(TOP, ROTATION); - } - - @Override - @SuppressWarnings("deprecation") - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - Vec3 vec3d = state.getOffset(view, pos); - return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); - } - - @Override - @SuppressWarnings("deprecation") - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - BlockState down = world.getBlockState(pos.below()); - BlockState up = world.getBlockState(pos.above()); - return state.getValue(TOP) ? down.getBlock() == this : isTerrain(down) && (up.getMaterial().isReplaceable()); - } - - public boolean canStayAt(BlockState state, LevelReader world, BlockPos pos) { - BlockState down = world.getBlockState(pos.below()); - BlockState up = world.getBlockState(pos.above()); - return state.getValue(TOP) ? down.getBlock() == this : isTerrain(down) && (up.getBlock() == this); - } - - protected abstract boolean isTerrain(BlockState state); - - @Override - @SuppressWarnings("deprecation") - public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { - if (!canStayAt(state, world, pos)) { - return Blocks.AIR.defaultBlockState(); - } - else { - return state; - } - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - if (state.getValue(TOP)) { - return Lists.newArrayList(); - } - - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - //TODO: 1.18.2 Test if shearing still works - if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( - Enchantments.SILK_TOUCH, - tool - ) > 0) { - return Lists.newArrayList(new ItemStack(this)); - } - else { - return Lists.newArrayList(); - } - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { - return true; - } - - @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { - return true; - } - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - ItemEntity item = new ItemEntity( - level, - pos.getX() + 0.5, - pos.getY() + 0.5, - pos.getZ() + 0.5, - new ItemStack(this) - ); - level.addFreshEntity(item); - } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { - int rot = world.random.nextInt(4); - BlockState bs = this.defaultBlockState().setValue(ROTATION, rot); - BlocksHelper.setWithoutUpdate(world, pos, bs); - BlocksHelper.setWithoutUpdate(world, pos.above(), bs.setValue(TOP, true)); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseFenceBlock.java b/src/main/java/ru/bclib/blocks/BaseFenceBlock.java deleted file mode 100644 index 8308330e..00000000 --- a/src/main/java/ru/bclib/blocks/BaseFenceBlock.java +++ /dev/null @@ -1,94 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.FenceBlock; -import net.minecraft.world.level.block.state.BlockState; -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.ModelsHelper.MultiPartBuilder; -import ru.bclib.client.models.PatternsHelper; -import ru.bclib.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseFenceBlock extends FenceBlock implements BlockModelProvider { - private final Block parent; - - public BaseFenceBlock(Block source) { - super(FabricBlockSettings.copyOf(source).noOcclusion()); - this.parent = source; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_FENCE, parentId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - String path = blockId.getPath(); - Optional pattern = Optional.empty(); - if (path.endsWith("_post")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FENCE_POST, parentId); - } - if (path.endsWith("_side")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FENCE_SIDE, parentId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_post"); - ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_side"); - registerBlockModel(postId, postId, blockState, modelCache); - registerBlockModel(sideId, sideId, blockState, modelCache); - - MultiPartBuilder builder = MultiPartBuilder.create(stateDefinition); - builder.part(sideId).setCondition(state -> state.getValue(NORTH)).setUVLock(true).add(); - builder.part(sideId) - .setCondition(state -> state.getValue(EAST)) - .setTransformation(BlockModelRotation.X0_Y90.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideId) - .setCondition(state -> state.getValue(SOUTH)) - .setTransformation(BlockModelRotation.X0_Y180.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideId) - .setCondition(state -> state.getValue(WEST)) - .setTransformation(BlockModelRotation.X0_Y270.getRotation()) - .setUVLock(true) - .add(); - builder.part(postId).add(); - - return builder.build(); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseFurnaceBlock.java b/src/main/java/ru/bclib/blocks/BaseFurnaceBlock.java deleted file mode 100644 index 52844196..00000000 --- a/src/main/java/ru/bclib/blocks/BaseFurnaceBlock.java +++ /dev/null @@ -1,133 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -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.client.resources.model.UnbakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.stats.Stats; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.FurnaceBlock; -import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityTicker; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import org.jetbrains.annotations.Nullable; -import ru.bclib.blockentities.BaseFurnaceBlockEntity; -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 ru.bclib.registry.BaseBlockEntities; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseFurnaceBlock extends FurnaceBlock implements BlockModelProvider, RenderLayerProvider { - public BaseFurnaceBlock(Block source) { - this(FabricBlockSettings.copyOf(source).lightLevel(state -> state.getValue(LIT) ? 13 : 0)); - } - - public BaseFurnaceBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - return new BaseFurnaceBlockEntity(blockPos, blockState); - } - - @Override - protected void openContainer(Level world, BlockPos pos, Player player) { - BlockEntity blockEntity = world.getBlockEntity(pos); - if (blockEntity instanceof BaseFurnaceBlockEntity) { - player.openMenu((MenuProvider) blockEntity); - player.awardStat(Stats.INTERACT_WITH_FURNACE); - } - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - String blockName = blockId.getPath(); - Map textures = Maps.newHashMap(); - textures.put("%modid%", blockId.getNamespace()); - textures.put("%top%", blockName + "_top"); - textures.put("%side%", blockName + "_side"); - Optional pattern; - if (blockState.getValue(LIT)) { - textures.put("%front%", blockName + "_front_on"); - textures.put("%glow%", blockName + "_glow"); - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FURNACE_LIT, textures); - } - else { - textures.put("%front%", blockName + "_front"); - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_FURNACE, textures); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - String lit = blockState.getValue(LIT) ? "_lit" : ""; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + lit); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), false, true); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - List drop = Lists.newArrayList(new ItemStack(this)); - BlockEntity blockEntity = builder.getOptionalParameter(LootContextParams.BLOCK_ENTITY); - if (blockEntity instanceof BaseFurnaceBlockEntity) { - BaseFurnaceBlockEntity entity = (BaseFurnaceBlockEntity) blockEntity; - for (int i = 0; i < entity.getContainerSize(); i++) { - drop.add(entity.getItem(i)); - } - } - return drop; - } - - @Override - @Nullable - public BlockEntityTicker getTicker(Level level, BlockState blockState, BlockEntityType blockEntityType) { - return createFurnaceTicker(level, blockEntityType, BaseBlockEntities.FURNACE); - } - - @Nullable - protected static BlockEntityTicker createFurnaceTicker(Level level, BlockEntityType blockEntityType, BlockEntityType blockEntityType2) { - return level.isClientSide ? null : createTickerHelper( - blockEntityType, - blockEntityType2, - AbstractFurnaceBlockEntity::serverTick - ); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseGateBlock.java b/src/main/java/ru/bclib/blocks/BaseGateBlock.java deleted file mode 100644 index b49e5b51..00000000 --- a/src/main/java/ru/bclib/blocks/BaseGateBlock.java +++ /dev/null @@ -1,78 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.FenceGateBlock; -import net.minecraft.world.level.block.state.BlockState; -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.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseGateBlock extends FenceGateBlock implements BlockModelProvider { - private final Block parent; - - public BaseGateBlock(Block source) { - super(FabricBlockSettings.copyOf(source).noOcclusion()); - this.parent = source; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - boolean inWall = blockState.getValue(IN_WALL); - boolean isOpen = blockState.getValue(OPEN); - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern; - if (inWall) { - pattern = isOpen ? PatternsHelper.createJson( - BasePatterns.BLOCK_GATE_OPEN_WALL, - parentId - ) : PatternsHelper.createJson(BasePatterns.BLOCK_GATE_CLOSED_WALL, parentId); - } - else { - pattern = isOpen ? PatternsHelper.createJson( - BasePatterns.BLOCK_GATE_OPEN, - parentId - ) : PatternsHelper.createJson(BasePatterns.BLOCK_GATE_CLOSED, parentId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - boolean inWall = blockState.getValue(IN_WALL); - boolean isOpen = blockState.getValue(OPEN); - String state = "" + (inWall ? "_wall" : "") + (isOpen ? "_open" : "_closed"); - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), true, false); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/BaseLadderBlock.java b/src/main/java/ru/bclib/blocks/BaseLadderBlock.java deleted file mode 100644 index 2128a780..00000000 --- a/src/main/java/ru/bclib/blocks/BaseLadderBlock.java +++ /dev/null @@ -1,68 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.LadderBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -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.Map; -import java.util.Optional; - -public class BaseLadderBlock extends LadderBlock implements RenderLayerProvider, BlockModelProvider { - public BaseLadderBlock(Block block) { - this(FabricBlockSettings.copyOf(block).noOcclusion()); - } - - public BaseLadderBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return ModelsHelper.createBlockItem(blockId); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_LADDER, blockId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), false, true); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseLeavesBlock.java b/src/main/java/ru/bclib/blocks/BaseLeavesBlock.java deleted file mode 100644 index 493513ce..00000000 --- a/src/main/java/ru/bclib/blocks/BaseLeavesBlock.java +++ /dev/null @@ -1,108 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -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.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.LeavesBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import ru.bclib.api.tag.NamedBlockTags; -import ru.bclib.api.tag.NamedItemTags; - -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.BlockModelProvider; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.interfaces.TagProvider; -import ru.bclib.interfaces.tools.AddMineableHoe; -import ru.bclib.interfaces.tools.AddMineableShears; -import ru.bclib.items.tool.BaseShearsItem; -import ru.bclib.util.MHelper; - -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -public class BaseLeavesBlock extends LeavesBlock implements BlockModelProvider, RenderLayerProvider, TagProvider, AddMineableShears, AddMineableHoe { - protected final Block sapling; - - private static FabricBlockSettings makeLeaves(MaterialColor color) { - return FabricBlockSettings - .copyOf(Blocks.OAK_LEAVES) - .mapColor(color) - //.requiresTool() - .allowsSpawning((state, world, pos, type) -> false) - .suffocates((state, world, pos) -> false) - .blockVision((state, world, pos) -> false); - } - - public BaseLeavesBlock(Block sapling, MaterialColor color, Consumer customizeProperties) { - super(BaseBlock.acceptAndReturn(customizeProperties, makeLeaves(color))); - this.sapling = sapling; - } - - public BaseLeavesBlock(Block sapling, MaterialColor color, int light, Consumer customizeProperties) { - super(BaseBlock.acceptAndReturn(customizeProperties, makeLeaves(color).luminance(light))); - this.sapling = sapling; - } - - public BaseLeavesBlock(Block sapling, MaterialColor color) { - super(makeLeaves(color)); - this.sapling = sapling; - } - - public BaseLeavesBlock(Block sapling, MaterialColor color, int light) { - super(makeLeaves(color).lightLevel(light)); - this.sapling = sapling; - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return BaseLeavesBlock.getLeaveDrops(this, this.sapling, builder, 16, 16); - } - - public static List getLeaveDrops(ItemLike leaveBlock, Block sapling, LootContext.Builder builder, int fortuneRate, int dropRate) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null) { - if (BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( - Enchantments.SILK_TOUCH, - tool - ) > 0) { - return Collections.singletonList(new ItemStack(leaveBlock)); - } - int fortune = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); - if (MHelper.RANDOM.nextInt(fortuneRate) <= fortune) { - return Lists.newArrayList(new ItemStack(sapling)); - } - return Lists.newArrayList(); - } - return MHelper.RANDOM.nextInt(dropRate) == 0 ? Lists.newArrayList(new ItemStack(sapling)) : Lists.newArrayList(); - } - - @Override - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - public void addTags(List> blockTags, List> itemTags) { - blockTags.add(NamedBlockTags.LEAVES); - itemTags.add(NamedItemTags.LEAVES); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseMetalBarsBlock.java b/src/main/java/ru/bclib/blocks/BaseMetalBarsBlock.java deleted file mode 100644 index 895ff38e..00000000 --- a/src/main/java/ru/bclib/blocks/BaseMetalBarsBlock.java +++ /dev/null @@ -1,125 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Direction; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.IronBarsBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -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.Map; -import java.util.Optional; - -public class BaseMetalBarsBlock extends IronBarsBlock implements BlockModelProvider, RenderLayerProvider { - public BaseMetalBarsBlock(Block source) { - this(FabricBlockSettings.copyOf(source).strength(5.0F, 6.0F).noOcclusion()); - } - - public BaseMetalBarsBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - public Optional getModelString(String block) { - ResourceLocation blockId = Registry.BLOCK.getKey(this); - if (block.contains("item")) { - return PatternsHelper.createJson(BasePatterns.ITEM_BLOCK, blockId); - } - if (block.contains("post")) { - return PatternsHelper.createJson(BasePatterns.BLOCK_BARS_POST, blockId); - } - else { - return PatternsHelper.createJson(BasePatterns.BLOCK_BARS_SIDE, blockId); - } - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createBlockItem(resourceLocation); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - ResourceLocation thisId = Registry.BLOCK.getKey(this); - String path = blockId.getPath(); - Optional pattern = Optional.empty(); - if (path.endsWith("_post")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BARS_POST, thisId); - } - if (path.endsWith("_side")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_BARS_SIDE, thisId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_post"); - ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_side"); - registerBlockModel(postId, postId, blockState, modelCache); - registerBlockModel(sideId, sideId, blockState, modelCache); - - ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); - builder.part(postId) - .setCondition(state -> !state.getValue(NORTH) && !state.getValue(EAST) && !state.getValue(SOUTH) && !state - .getValue(WEST)) - .add(); - builder.part(sideId).setCondition(state -> state.getValue(NORTH)).setUVLock(true).add(); - builder.part(sideId) - .setCondition(state -> state.getValue(EAST)) - .setTransformation(BlockModelRotation.X0_Y90.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideId) - .setCondition(state -> state.getValue(SOUTH)) - .setTransformation(BlockModelRotation.X0_Y180.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideId) - .setCondition(state -> state.getValue(WEST)) - .setTransformation(BlockModelRotation.X0_Y270.getRotation()) - .setUVLock(true) - .add(); - - return builder.build(); - } - - @Environment(EnvType.CLIENT) - public boolean skipRendering(BlockState state, BlockState stateFrom, Direction direction) { - if (direction.getAxis().isVertical() && stateFrom.getBlock() == this && !stateFrom.equals(state)) { - return false; - } - return super.skipRendering(state, stateFrom, direction); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseOreBlock.java b/src/main/java/ru/bclib/blocks/BaseOreBlock.java deleted file mode 100644 index 2bd04a12..00000000 --- a/src/main/java/ru/bclib/blocks/BaseOreBlock.java +++ /dev/null @@ -1,106 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; -import net.minecraft.util.valueproviders.UniformInt; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TieredItem; -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.DropExperienceBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import ru.bclib.interfaces.BlockModelProvider; -import ru.bclib.util.LootUtil; -import ru.bclib.util.MHelper; - -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; - -public class BaseOreBlock extends DropExperienceBlock implements BlockModelProvider { - private final Supplier dropItem; - private final int minCount; - private final int maxCount; - private final int miningLevel; - - public BaseOreBlock(Supplier drop, int minCount, int maxCount, int experience) { - this(drop, minCount, maxCount, experience, 0); - } - - public BaseOreBlock(Supplier drop, int minCount, int maxCount, int experience, int miningLevel) { - this( - FabricBlockSettings - .of(Material.STONE, MaterialColor.SAND) - .requiresTool() - .destroyTime(3F) - .explosionResistance(9F) - .sound(SoundType.STONE), - drop, minCount, maxCount, experience, miningLevel - ); - } - - public BaseOreBlock(Properties properties, Supplier drop, int minCount, int maxCount, int experience) { - this(properties, drop, minCount, maxCount, experience, 0); - } - - public BaseOreBlock(Properties properties, Supplier drop, int minCount, int maxCount, int experience, int miningLevel) { - super(properties, UniformInt.of(experience>0?1:0, experience)); - this.dropItem = drop; - this.minCount = minCount; - this.maxCount = maxCount; - this.miningLevel = miningLevel; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return LootUtil - .getDrops(this, state, builder) - .orElseGet( - ()->BaseOreBlock.getDroppedItems(this, dropItem.get(), maxCount, minCount, miningLevel, state, builder) - ); - } - - public static List getDroppedItems(ItemLike block, Item dropItem, int maxCount, int minCount, int miningLevel, BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && tool.isCorrectToolForDrops(state)) { - boolean canMine = miningLevel==0; - if (tool.getItem() instanceof TieredItem tired) { - canMine = tired.getTier().getLevel()>=miningLevel; - } - if (canMine) { - if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { - return Collections.singletonList(new ItemStack(block)); - } - int count; - int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); - if (enchantment > 0) { - int min = Mth.clamp(minCount + enchantment, minCount, maxCount); - int max = maxCount + (enchantment / Enchantments.BLOCK_FORTUNE.getMaxLevel()); - if (min == max) { - return Collections.singletonList(new ItemStack(dropItem, max)); - } - count = MHelper.randRange(min, max, MHelper.RANDOM_SOURCE); - } else { - count = MHelper.randRange(minCount, maxCount, MHelper.RANDOM_SOURCE); - } - return Collections.singletonList(new ItemStack(dropItem, count)); - } - } - return Collections.emptyList(); - } - - @Override - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } -} diff --git a/src/main/java/ru/bclib/blocks/BasePathBlock.java b/src/main/java/ru/bclib/blocks/BasePathBlock.java deleted file mode 100644 index c0f212ef..00000000 --- a/src/main/java/ru/bclib/blocks/BasePathBlock.java +++ /dev/null @@ -1,97 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Maps; -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.client.resources.model.UnbakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -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.BlockGetter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -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 java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BasePathBlock extends BaseBlockNotFull { - private static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 15, 16); - - private Block baseBlock; - - public BasePathBlock(Block source) { - super(FabricBlockSettings.copyOf(source).isValidSpawn((state, world, pos, type) -> false)); - this.baseBlock = Blocks.DIRT; - if (source instanceof BaseTerrainBlock) { - BaseTerrainBlock terrain = (BaseTerrainBlock) source; - this.baseBlock = terrain.getBaseBlock(); - terrain.setPathBlock(this); - } - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { - return Collections.singletonList(new ItemStack(this)); - } - return Collections.singletonList(new ItemStack(Blocks.END_STONE)); - } - - @Override - @SuppressWarnings("deprecation") - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPE; - } - - @Override - @SuppressWarnings("deprecation") - public VoxelShape getCollisionShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPE; - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - String name = blockId.getPath(); - ResourceLocation bottomId = Registry.BLOCK.getKey(baseBlock); - String bottom = bottomId.getNamespace() + ":block/" + bottomId.getPath(); - Map textures = Maps.newHashMap(); - textures.put("%modid%", blockId.getNamespace()); - textures.put("%top%", name + "_top"); - textures.put("%side%", name.replace("_path", "") + "_side"); - textures.put("%bottom%", bottom); - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PATH, textures); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createRandomTopModel(modelId); - } -} diff --git a/src/main/java/ru/bclib/blocks/BasePlantBlock.java b/src/main/java/ru/bclib/blocks/BasePlantBlock.java deleted file mode 100644 index 8d6139f3..00000000 --- a/src/main/java/ru/bclib/blocks/BasePlantBlock.java +++ /dev/null @@ -1,177 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -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.entity.item.ItemEntity; -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.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.BonemealableBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.Vec3; -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.RenderLayerProvider; -import ru.bclib.items.tool.BaseShearsItem; - -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.function.Function; - -import net.minecraft.util.RandomSource; - -public abstract class BasePlantBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock{ - private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); - - public BasePlantBlock() { - this(false, p->p); - } - - public BasePlantBlock(int light) { - this(light, p->p); - } - - public BasePlantBlock(int light, Function propMod) { - this(false, light, propMod); - } - - public BasePlantBlock(boolean replaceabled) { - this(replaceabled, p->p); - } - public BasePlantBlock(boolean replaceable, Function propMod) { - this( - propMod.apply(FabricBlockSettings - .of(replaceable ? Material.REPLACEABLE_PLANT : Material.PLANT) - .sound(SoundType.GRASS) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ) - ) - ); - } - - public BasePlantBlock(boolean replaceable, int light){ - this(replaceable, light, p->p); - } - public BasePlantBlock(boolean replaceable, int light, Function propMod) { - this( - propMod.apply(FabricBlockSettings - .of(replaceable ? Material.REPLACEABLE_PLANT : Material.PLANT) - .luminance(light) - .sound(SoundType.GRASS) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ) - ) - ); - } - - public BasePlantBlock(Properties settings) { - super(settings.offsetType(BlockBehaviour.OffsetType.XZ)); - } - - protected abstract boolean isTerrain(BlockState state); - - @Override - @SuppressWarnings("deprecation") - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - Vec3 vec3d = state.getOffset(view, pos); - return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); - } - - @Override - @SuppressWarnings("deprecation") - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - BlockState down = world.getBlockState(pos.below()); - return isTerrain(down); - } - - @Override - @SuppressWarnings("deprecation") - 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 List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - //TODO: 1.18.2 Test if shearing still works - if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( - Enchantments.SILK_TOUCH, - tool - ) > 0) { - return Lists.newArrayList(new ItemStack(this)); - } - else { - return Lists.newArrayList(); - } - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { - return true; - } - - @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { - return true; - } - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - ItemEntity item = new ItemEntity( - level, - pos.getX() + 0.5, - pos.getY() + 0.5, - pos.getZ() + 0.5, - new ItemStack(this) - ); - level.addFreshEntity(item); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createBlockItem(resourceLocation); - } - - @Override - @Nullable - @Environment(EnvType.CLIENT) - public BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS, resourceLocation); - return ModelsHelper.fromPattern(pattern); - } -} diff --git a/src/main/java/ru/bclib/blocks/BasePlantWithAgeBlock.java b/src/main/java/ru/bclib/blocks/BasePlantWithAgeBlock.java deleted file mode 100644 index 22f14454..00000000 --- a/src/main/java/ru/bclib/blocks/BasePlantWithAgeBlock.java +++ /dev/null @@ -1,72 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.material.Material; - -import java.util.Properties; -import java.util.Random; -import java.util.function.Function; - -import net.minecraft.util.RandomSource; - -public abstract class BasePlantWithAgeBlock extends BasePlantBlock { - public static final IntegerProperty AGE = BlockProperties.AGE; - - public BasePlantWithAgeBlock() { - this(p->p); - } - - public BasePlantWithAgeBlock(Function propMod) { - this( - propMod.apply(FabricBlockSettings.of(Material.PLANT) - .sound(SoundType.GRASS) - .randomTicks() - .noCollission()) - ); - } - - public BasePlantWithAgeBlock(Properties settings) { - super(settings); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(AGE); - } - - public abstract void growAdult(WorldGenLevel world, RandomSource random, BlockPos pos); - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - int age = state.getValue(AGE); - if (age < 3) { - level.setBlockAndUpdate(pos, state.setValue(AGE, age + 1)); - } - else { - growAdult(level, random, pos); - } - } - - @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { - return true; - } - - @Override - @SuppressWarnings("deprecation") - public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - super.tick(state, world, pos, random); - if (random.nextInt(8) == 0) { - performBonemeal(world, random, pos, state); - } - } -} diff --git a/src/main/java/ru/bclib/blocks/BasePressurePlateBlock.java b/src/main/java/ru/bclib/blocks/BasePressurePlateBlock.java deleted file mode 100644 index 77d2cacb..00000000 --- a/src/main/java/ru/bclib/blocks/BasePressurePlateBlock.java +++ /dev/null @@ -1,68 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.PressurePlateBlock; -import net.minecraft.world.level.block.state.BlockState; -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.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BasePressurePlateBlock extends PressurePlateBlock implements BlockModelProvider { - private final Block parent; - - public BasePressurePlateBlock(Sensitivity rule, Block source) { - super(rule, FabricBlockSettings.copyOf(source).noCollission().noOcclusion().strength(0.5F)); - this.parent = source; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern; - if (blockState.getValue(POWERED)) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_DOWN, parentId); - } - else { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_UP, parentId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - String state = blockState.getValue(POWERED) ? "_down" : "_up"; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createBlockSimple(modelId); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseRotatedPillarBlock.java b/src/main/java/ru/bclib/blocks/BaseRotatedPillarBlock.java deleted file mode 100644 index 1b4f14e7..00000000 --- a/src/main/java/ru/bclib/blocks/BaseRotatedPillarBlock.java +++ /dev/null @@ -1,63 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.RotatedPillarBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import org.jetbrains.annotations.Nullable; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; -import ru.bclib.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseRotatedPillarBlock extends RotatedPillarBlock implements BlockModelProvider { - public BaseRotatedPillarBlock(Properties settings) { - super(settings); - } - - public BaseRotatedPillarBlock(Block block) { - this(FabricBlockSettings.copyOf(block)); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - Optional pattern = createBlockPattern(blockId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createRotatedModel(modelId, blockState.getValue(AXIS)); - } - - protected Optional createBlockPattern(ResourceLocation blockId) { - return PatternsHelper.createBlockPillar(blockId); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseSignBlock.java b/src/main/java/ru/bclib/blocks/BaseSignBlock.java deleted file mode 100644 index cb07c88e..00000000 --- a/src/main/java/ru/bclib/blocks/BaseSignBlock.java +++ /dev/null @@ -1,192 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -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.core.Registry; -import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.SignBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.block.state.properties.WoodType; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -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.blockentities.BaseSignBlockEntity; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.BlockModelProvider; -import ru.bclib.interfaces.CustomItemProvider; -import ru.bclib.util.BlocksHelper; - -import java.util.Collections; -import java.util.List; - -@SuppressWarnings("deprecation") -public class BaseSignBlock extends SignBlock implements BlockModelProvider, CustomItemProvider { - public static final IntegerProperty ROTATION = BlockStateProperties.ROTATION_16; - public static final BooleanProperty FLOOR = BooleanProperty.create("floor"); - private static final VoxelShape[] WALL_SHAPES = new VoxelShape[] { - Block.box(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D), - Block.box(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D), - Block.box(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D), - Block.box(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D) - }; - - private final Block parent; - - public BaseSignBlock(Block source) { - super(FabricBlockSettings.copyOf(source).strength(1.0F, 1.0F).noCollission().noOcclusion(), WoodType.OAK); - this.registerDefaultState(this.stateDefinition.any().setValue(ROTATION, 0).setValue(FLOOR, false).setValue(WATERLOGGED, false)); - this.parent = source; - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(ROTATION, FLOOR, WATERLOGGED); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return state.getValue(FLOOR) ? SHAPE : WALL_SHAPES[state.getValue(ROTATION) >> 2]; - } - - @Override - public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - return new BaseSignBlockEntity(blockPos, blockState); - } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { - if (placer instanceof Player) { - BaseSignBlockEntity sign = (BaseSignBlockEntity) world.getBlockEntity(pos); - if (sign != null) { - if (!world.isClientSide) { - sign.setAllowedPlayerEditor(placer.getUUID()); - ((ServerPlayer) placer).connection.send(new ClientboundOpenSignEditorPacket(pos)); - } - else { - sign.setEditable(true); - } - } - } - } - - @Override - public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { - if (state.getValue(WATERLOGGED)) { - world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); - } - if (!canSurvive(state, world, pos)) { - return state.getValue(WATERLOGGED) ? state.getFluidState().createLegacyBlock() : Blocks.AIR.defaultBlockState(); - } - return super.updateShape(state, facing, neighborState, world, pos, neighborPos); - } - - @Override - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - if (!state.getValue(FLOOR)) { - int index = (((state.getValue(ROTATION) >> 2) + 2)) & 3; - return world.getBlockState(pos.relative(BlocksHelper.HORIZONTAL[index])).getMaterial().isSolid(); - } - else { - return world.getBlockState(pos.below()).getMaterial().isSolid(); - } - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext ctx) { - if (ctx.getClickedFace() == Direction.UP) { - FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); - return this - .defaultBlockState() - .setValue(FLOOR, true) - .setValue(ROTATION, Mth.floor((180.0 + ctx.getRotation() * 16.0 / 360.0) + 0.5 - 12) & 15) - .setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); - } - else if (ctx.getClickedFace() != Direction.DOWN) { - BlockState blockState = this.defaultBlockState(); - FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); - LevelReader worldView = ctx.getLevel(); - BlockPos blockPos = ctx.getClickedPos(); - Direction[] directions = ctx.getNearestLookingDirections(); - - for (Direction direction : directions) { - if (direction.getAxis().isHorizontal()) { - Direction dir = direction.getOpposite(); - int rot = Mth.floor((180.0 + dir.toYRot() * 16.0 / 360.0) + 0.5 + 4) & 15; - blockState = blockState.setValue(ROTATION, rot); - if (blockState.canSurvive(worldView, blockPos)) { - return blockState.setValue(FLOOR, false).setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); - } - } - } - } - - return null; - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - return ModelsHelper.createBlockEmpty(parentId); - } - - @Override - public BlockState rotate(BlockState state, Rotation rotation) { - return state.setValue(ROTATION, rotation.rotate(state.getValue(ROTATION), 16)); - } - - @Override - public BlockState mirror(BlockState state, Mirror mirror) { - return state.setValue(ROTATION, mirror.mirror(state.getValue(ROTATION), 16)); - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { - return super.canPlaceLiquid(world, pos, state, fluid); - } - - @Override - public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { - return super.placeLiquid(world, pos, state, fluidState); - } - - @Override - public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { - return new BlockItem(this, settings.stacksTo(16)); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/BaseSlabBlock.java b/src/main/java/ru/bclib/blocks/BaseSlabBlock.java deleted file mode 100644 index 14d5eb24..00000000 --- a/src/main/java/ru/bclib/blocks/BaseSlabBlock.java +++ /dev/null @@ -1,92 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SlabBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.SlabType; -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.interfaces.BlockModelProvider; -import ru.bclib.interfaces.CustomItemProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseSlabBlock extends SlabBlock implements BlockModelProvider, CustomItemProvider { - private final Block parent; - public final boolean fireproof; - - public BaseSlabBlock(Block source) { - this(source, false); - } - - public BaseSlabBlock(Block source, boolean fireproof) { - super(FabricBlockSettings.copyOf(source)); - this.parent = source; - this.fireproof = fireproof; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - int count = state.getValue(TYPE) == SlabType.DOUBLE ? 2 : 1; - return Collections.singletonList(new ItemStack(this, count)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern; - if (blockState.getValue(TYPE) == SlabType.DOUBLE) { - pattern = PatternsHelper.createBlockSimple(parentId); - } - else { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_SLAB, parentId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - SlabType type = blockState.getValue(TYPE); - ResourceLocation modelId = new ResourceLocation( - stateId.getNamespace(), - "block/" + stateId.getPath() + "_" + type - ); - registerBlockModel(stateId, modelId, blockState, modelCache); - if (type == SlabType.TOP) { - return ModelsHelper.createMultiVariant(modelId, BlockModelRotation.X180_Y0.getRotation(), true); - } - return ModelsHelper.createBlockSimple(modelId); - } - - @Override - public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { - if (fireproof) settings = settings.fireproof(); - return new BlockItem(this, settings); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseStairsBlock.java b/src/main/java/ru/bclib/blocks/BaseStairsBlock.java deleted file mode 100644 index b3e8e69a..00000000 --- a/src/main/java/ru/bclib/blocks/BaseStairsBlock.java +++ /dev/null @@ -1,115 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.StairBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Half; -import net.minecraft.world.level.block.state.properties.StairsShape; -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.interfaces.BlockModelProvider; -import ru.bclib.interfaces.CustomItemProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseStairsBlock extends StairBlock implements BlockModelProvider, CustomItemProvider { - private final Block parent; - public final boolean fireproof; - - public BaseStairsBlock(Block source) { - this(source, false); - } - - public BaseStairsBlock(Block source, boolean fireproof) { - super(source.defaultBlockState(), FabricBlockSettings.copyOf(source)); - this.parent = source; - this.fireproof = fireproof; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern = PatternsHelper.createJson(switch (blockState.getValue(SHAPE)) { - case STRAIGHT -> BasePatterns.BLOCK_STAIR; - case INNER_LEFT, INNER_RIGHT -> BasePatterns.BLOCK_STAIR_INNER; - case OUTER_LEFT, OUTER_RIGHT -> BasePatterns.BLOCK_STAIR_OUTER; - }, parentId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - String state; - StairsShape shape = blockState.getValue(SHAPE); - state = switch (shape) { - case INNER_LEFT, INNER_RIGHT -> "_inner"; - case OUTER_LEFT, OUTER_RIGHT -> "_outer"; - default -> ""; - }; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); - registerBlockModel(stateId, modelId, blockState, modelCache); - - boolean isTop = blockState.getValue(HALF) == Half.TOP; - boolean isLeft = shape == StairsShape.INNER_LEFT || shape == StairsShape.OUTER_LEFT; - boolean isRight = shape == StairsShape.INNER_RIGHT || shape == StairsShape.OUTER_RIGHT; - int y = 0; - int x = isTop ? 180 : 0; - switch (blockState.getValue(FACING)) { - case NORTH: - if (isTop && !isRight) y = 270; - else if (!isTop) y = isLeft ? 180 : 270; - break; - case EAST: - if (isTop && isRight) y = 90; - else if (!isTop && isLeft) y = 270; - break; - case SOUTH: - if (isTop) y = isRight ? 180 : 90; - else if (!isLeft) y = 90; - break; - case WEST: - default: - y = (isTop && isRight) ? 270 : (!isTop && isLeft) ? 90 : 180; - break; - } - BlockModelRotation rotation = BlockModelRotation.by(x, y); - return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), true); - } - - @Override - public BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings) { - if (fireproof) settings = settings.fireproof(); - return new BlockItem(this, settings); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseStoneButtonBlock.java b/src/main/java/ru/bclib/blocks/BaseStoneButtonBlock.java deleted file mode 100644 index ed10c5a5..00000000 --- a/src/main/java/ru/bclib/blocks/BaseStoneButtonBlock.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.world.level.block.Block; - -public class BaseStoneButtonBlock extends BaseButtonBlock { - public BaseStoneButtonBlock(Block source) { - super(source, FabricBlockSettings.copyOf(source).noOcclusion(), false); - } - - @Override - protected SoundEvent getSound(boolean clicked) { - return clicked ? SoundEvents.STONE_BUTTON_CLICK_ON : SoundEvents.STONE_BUTTON_CLICK_OFF; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseStripableLogBlock.java b/src/main/java/ru/bclib/blocks/BaseStripableLogBlock.java deleted file mode 100644 index 9d2b9d46..00000000 --- a/src/main/java/ru/bclib/blocks/BaseStripableLogBlock.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.RotatedPillarBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.phys.BlockHitResult; -import ru.bclib.api.tag.NamedMineableTags; -import ru.bclib.api.tag.TagAPI; - -public class BaseStripableLogBlock extends BaseRotatedPillarBlock { - private final Block striped; - - public BaseStripableLogBlock(MaterialColor color, Block striped) { - super(FabricBlockSettings.copyOf(striped).color(color)); - this.striped = striped; - } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - if (TagAPI.isToolWithMineableTag(player.getMainHandItem(), NamedMineableTags.AXE)){ - world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); - if (!world.isClientSide) { - world.setBlock(pos, - striped.defaultBlockState() - .setValue(RotatedPillarBlock.AXIS, state.getValue(RotatedPillarBlock.AXIS)), - 11 - ); - if (!player.isCreative()) { - player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); - } - } - return InteractionResult.SUCCESS; - } - return InteractionResult.FAIL; - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseTerrainBlock.java b/src/main/java/ru/bclib/blocks/BaseTerrainBlock.java deleted file mode 100644 index cd7543ee..00000000 --- a/src/main/java/ru/bclib/blocks/BaseTerrainBlock.java +++ /dev/null @@ -1,156 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Maps; -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.client.resources.model.UnbakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -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.Level; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.SnowLayerBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.lighting.LayerLightEngine; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.BlockHitResult; -import org.jetbrains.annotations.Nullable; -import ru.bclib.api.tag.NamedMineableTags; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.client.models.BasePatterns; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; -import ru.bclib.client.sound.BlockSounds; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Random;import net.minecraft.util.RandomSource; - -@SuppressWarnings("deprecation") -public class BaseTerrainBlock extends BaseBlock { - private final Block baseBlock; - private Block pathBlock; - - public BaseTerrainBlock(Block baseBlock, MaterialColor color) { - super(FabricBlockSettings - .copyOf(baseBlock) - .materialColor(color) - .sound(BlockSounds.TERRAIN_SOUND) - .randomTicks() - ); - this.baseBlock = baseBlock; - } - - public void setPathBlock(Block roadBlock) { - this.pathBlock = roadBlock; - } - - public Block getBaseBlock() { - return baseBlock; - } - - @Override - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - //TODO: 1.18.2 check - if (pathBlock != null && TagAPI.isToolWithMineableTag(player.getMainHandItem(), NamedMineableTags.SHOVEL)){ - //if (pathBlock != null && FabricTagProvider.SHOVELS.contains(player.getMainHandItem().getItem())) { - world.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); - if (!world.isClientSide) { - world.setBlockAndUpdate(pos, pathBlock.defaultBlockState()); - if (!player.isCreative()) { - player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); - } - } - return InteractionResult.SUCCESS; - } - return InteractionResult.FAIL; - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { - return Collections.singletonList(new ItemStack(this)); - } - return Collections.singletonList(new ItemStack(getBaseBlock())); - } - - @Override - public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - if (random.nextInt(16) == 0 && !canStay(state, world, pos)) { - world.setBlockAndUpdate(pos, getBaseBlock().defaultBlockState()); - } - } - - public boolean canStay(BlockState state, LevelReader worldView, BlockPos pos) { - BlockPos blockPos = pos.above(); - BlockState blockState = worldView.getBlockState(blockPos); - if (blockState.is(Blocks.SNOW) && blockState.getValue(SnowLayerBlock.LAYERS) == 1) { - return true; - } - else if (blockState.getFluidState().getAmount() == 8) { - return false; - } - else { - int i = LayerLightEngine.getLightBlockInto( - worldView, - state, - pos, - blockState, - blockPos, - Direction.UP, - blockState.getLightBlock(worldView, blockPos) - ); - return i < 5; - } - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - ResourceLocation baseId = Registry.BLOCK.getKey(getBaseBlock()); - String modId = blockId.getNamespace(); - String path = blockId.getPath(); - String bottom = baseId.getNamespace() + ":block/" + baseId.getPath(); - Map textures = Maps.newHashMap(); - textures.put("%top%", modId + ":block/" + path + "_top"); - textures.put("%side%", modId + ":block/" + path + "_side"); - textures.put("%bottom%", bottom); - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_TOP_SIDE_BOTTOM, textures); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createRandomTopModel(modelId); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseTrapdoorBlock.java b/src/main/java/ru/bclib/blocks/BaseTrapdoorBlock.java deleted file mode 100644 index 7f09fee6..00000000 --- a/src/main/java/ru/bclib/blocks/BaseTrapdoorBlock.java +++ /dev/null @@ -1,104 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.TrapDoorBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Half; -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.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseTrapdoorBlock extends TrapDoorBlock implements RenderLayerProvider, BlockModelProvider { - public BaseTrapdoorBlock(Block source) { - this(FabricBlockSettings.copyOf(source).strength(3.0F, 3.0F).noOcclusion()); - } - - public BaseTrapdoorBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - String name = resourceLocation.getPath(); - Optional pattern = PatternsHelper.createJson( - BasePatterns.BLOCK_TRAPDOOR, - new HashMap() { - private static final long serialVersionUID = 1L; - - { - put("%modid%", resourceLocation.getNamespace()); - put("%texture%", name); - put("%side%", name.replace("trapdoor", "door_side")); - } - } - ); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - boolean isTop = blockState.getValue(HALF) == Half.TOP; - boolean isOpen = blockState.getValue(OPEN); - int y = 0; - int x = (isTop && isOpen) ? 270 : isTop ? 180 : isOpen ? 90 : 0; - switch (blockState.getValue(FACING)) { - case EAST: - y = (isTop && isOpen) ? 270 : 90; - break; - case NORTH: - if (isTop && isOpen) y = 180; - break; - case SOUTH: - y = (isTop && isOpen) ? 0 : 180; - break; - case WEST: - y = (isTop && isOpen) ? 90 : 270; - break; - default: - break; - } - BlockModelRotation rotation = BlockModelRotation.by(x, y); - return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseUnderwaterWallPlantBlock.java b/src/main/java/ru/bclib/blocks/BaseUnderwaterWallPlantBlock.java deleted file mode 100644 index c6351c6a..00000000 --- a/src/main/java/ru/bclib/blocks/BaseUnderwaterWallPlantBlock.java +++ /dev/null @@ -1,60 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.LiquidBlockContainer; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -import net.minecraft.world.level.material.Material; - -public abstract class BaseUnderwaterWallPlantBlock extends BaseWallPlantBlock implements LiquidBlockContainer { - public BaseUnderwaterWallPlantBlock() { - this( - FabricBlockSettings - .of(Material.WATER_PLANT) - .sound(SoundType.WET_GRASS) - .noCollission() - ); - } - - public BaseUnderwaterWallPlantBlock(int light) { - this( - FabricBlockSettings - .of(Material.WATER_PLANT) - .luminance(light) - .sound(SoundType.WET_GRASS) - .noCollission() - ); - } - - public BaseUnderwaterWallPlantBlock(Properties settings) { - super(settings); - } - - @Override - public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { - return false; - } - - @Override - public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { - return false; - } - - @Override - @SuppressWarnings("deprecation") - public FluidState getFluidState(BlockState state) { - return Fluids.WATER.getSource(false); - } - - @Override - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - return world.getFluidState(pos).getType() == Fluids.WATER && super.canSurvive(state, world, pos); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseVineBlock.java b/src/main/java/ru/bclib/blocks/BaseVineBlock.java deleted file mode 100644 index ff830008..00000000 --- a/src/main/java/ru/bclib/blocks/BaseVineBlock.java +++ /dev/null @@ -1,154 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.tags.BlockTags; -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.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.BonemealableBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.EnumProperty; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; -import ru.bclib.blocks.BlockProperties.TripleShape; -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.items.tool.BaseShearsItem; -import ru.bclib.util.BlocksHelper; - -import java.util.List; -import java.util.Random; -import java.util.function.Function; - -import net.minecraft.util.RandomSource; - -@SuppressWarnings("deprecation") -public class BaseVineBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock { - public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; - private static final VoxelShape VOXEL_SHAPE = Block.box(2, 0, 2, 14, 16, 14); - - public BaseVineBlock() { - this(0, false); - } - - public BaseVineBlock(int light) { - this(light, false); - } - - public BaseVineBlock(int light, boolean bottomOnly){ - this(light, bottomOnly, p->p); - } - public BaseVineBlock(int light, boolean bottomOnly, Function propMod) { - this( - propMod.apply(FabricBlockSettings - .of(Material.PLANT) - .sound(SoundType.GRASS) - .lightLevel((state) -> bottomOnly ? state.getValue(SHAPE) == TripleShape.BOTTOM ? light : 0 : light) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ)) - ); - } - - public BaseVineBlock(BlockBehaviour.Properties properties) { - super(properties.offsetType(BlockBehaviour.OffsetType.XZ)); - this.registerDefaultState(this.stateDefinition.any().setValue(SHAPE, TripleShape.BOTTOM)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(SHAPE); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - Vec3 vec3d = state.getOffset(view, pos); - return VOXEL_SHAPE.move(vec3d.x, vec3d.y, vec3d.z); - } - - public boolean canGenerate(BlockState state, LevelReader world, BlockPos pos) { - return isSupport(state, world, pos); - } - - @Override - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - return isSupport(state, world, pos); - } - - protected boolean isSupport(BlockState state, LevelReader world, BlockPos pos) { - BlockState up = world.getBlockState(pos.above()); - return up.is(this) || up.is(BlockTags.LEAVES) || canSupportCenter(world, pos.above(), Direction.DOWN); - } - - @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 { - if (world.getBlockState(pos.below()).getBlock() != this) return state.setValue(SHAPE, TripleShape.BOTTOM); - else if (world.getBlockState(pos.above()).getBlock() != this) return state.setValue(SHAPE, TripleShape.TOP); - return state.setValue(SHAPE, TripleShape.MIDDLE); - } - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( - Enchantments.SILK_TOUCH, - tool - ) > 0) { - return Lists.newArrayList(new ItemStack(this)); - } - else { - return Lists.newArrayList(); - } - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { - while (world.getBlockState(pos).getBlock() == this) { - pos = pos.below(); - } - return world.getBlockState(pos).isAir(); - } - - @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { - while (level.getBlockState(pos).getBlock() == this) { - pos = pos.below(); - } - return level.isEmptyBlock(pos); - } - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - while (level.getBlockState(pos).getBlock() == this) { - pos = pos.below(); - } - level.setBlockAndUpdate(pos, defaultBlockState()); - BlocksHelper.setWithoutUpdate(level, pos, defaultBlockState()); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseWallBlock.java b/src/main/java/ru/bclib/blocks/BaseWallBlock.java deleted file mode 100644 index 2081d5f6..00000000 --- a/src/main/java/ru/bclib/blocks/BaseWallBlock.java +++ /dev/null @@ -1,121 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.WallBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.WallSide; -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.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseWallBlock extends WallBlock implements BlockModelProvider { - private final Block parent; - - public BaseWallBlock(Block source) { - super(FabricBlockSettings.copyOf(source).noOcclusion()); - this.parent = source; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_WALL, parentId); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - String path = blockId.getPath(); - Optional pattern = Optional.empty(); - if (path.endsWith("_post")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_WALL_POST, parentId); - } - if (path.endsWith("_side")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_WALL_SIDE, parentId); - } - if (path.endsWith("_side_tall")) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_WALL_SIDE_TALL, parentId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_post"); - ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + "_side"); - ResourceLocation sideTallId = new ResourceLocation( - stateId.getNamespace(), - "block/" + stateId.getPath() + "_side_tall" - ); - registerBlockModel(postId, postId, blockState, modelCache); - registerBlockModel(sideId, sideId, blockState, modelCache); - registerBlockModel(sideTallId, sideTallId, blockState, modelCache); - - ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); - builder.part(sideId).setCondition(state -> state.getValue(NORTH_WALL) == WallSide.LOW).setUVLock(true).add(); - builder.part(sideId) - .setCondition(state -> state.getValue(EAST_WALL) == WallSide.LOW) - .setTransformation(BlockModelRotation.X0_Y90.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideId) - .setCondition(state -> state.getValue(SOUTH_WALL) == WallSide.LOW) - .setTransformation(BlockModelRotation.X0_Y180.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideId) - .setCondition(state -> state.getValue(WEST_WALL) == WallSide.LOW) - .setTransformation(BlockModelRotation.X0_Y270.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideTallId) - .setCondition(state -> state.getValue(NORTH_WALL) == WallSide.TALL) - .setUVLock(true) - .add(); - builder.part(sideTallId) - .setCondition(state -> state.getValue(EAST_WALL) == WallSide.TALL) - .setTransformation(BlockModelRotation.X0_Y90.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideTallId) - .setCondition(state -> state.getValue(SOUTH_WALL) == WallSide.TALL) - .setTransformation(BlockModelRotation.X0_Y180.getRotation()) - .setUVLock(true) - .add(); - builder.part(sideTallId) - .setCondition(state -> state.getValue(WEST_WALL) == WallSide.TALL) - .setTransformation(BlockModelRotation.X0_Y270.getRotation()) - .setUVLock(true) - .add(); - builder.part(postId).setCondition(state -> state.getValue(UP)).add(); - - return builder.build(); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseWallPlantBlock.java b/src/main/java/ru/bclib/blocks/BaseWallPlantBlock.java deleted file mode 100644 index 1879d48d..00000000 --- a/src/main/java/ru/bclib/blocks/BaseWallPlantBlock.java +++ /dev/null @@ -1,124 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.HorizontalDirectionalBlock; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.DirectionProperty; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; -import ru.bclib.util.BlocksHelper; - -import java.util.EnumMap; - -public abstract class BaseWallPlantBlock extends BasePlantBlock { - private static final EnumMap SHAPES = Maps.newEnumMap(ImmutableMap.of( - Direction.NORTH, Block.box(1, 1, 8, 15, 15, 16), - Direction.SOUTH, Block.box(1, 1, 0, 15, 15, 8), - Direction.WEST, Block.box(8, 1, 1, 16, 15, 15), - Direction.EAST, Block.box(0, 1, 1, 8, 15, 15) - )); - public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; - - public BaseWallPlantBlock() { - this( - FabricBlockSettings - .of(Material.PLANT) - .sound(SoundType.GRASS) - .noCollission() - .offsetType( BlockBehaviour.OffsetType.NONE) - ); - } - - public BaseWallPlantBlock(int light) { - this( - FabricBlockSettings - .of(Material.PLANT) - .luminance(light) - .sound(SoundType.GRASS) - .noCollission() - .offsetType( BlockBehaviour.OffsetType.NONE) - ); - } - - public BaseWallPlantBlock(Properties settings) { - super(settings.offsetType( BlockBehaviour.OffsetType.NONE)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(FACING); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPES.get(state.getValue(FACING)); - } - - @Override - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - Direction direction = state.getValue(FACING); - BlockPos blockPos = pos.relative(direction.getOpposite()); - BlockState blockState = world.getBlockState(blockPos); - return isSupport(world, blockPos, blockState, direction); - } - - public boolean isSupport(LevelReader world, BlockPos pos, BlockState blockState, Direction direction) { - return blockState.getMaterial().isSolid() && blockState.isFaceSturdy(world, pos, direction); - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext ctx) { - BlockState blockState = this.defaultBlockState(); - LevelReader worldView = ctx.getLevel(); - BlockPos blockPos = ctx.getClickedPos(); - Direction[] directions = ctx.getNearestLookingDirections(); - for (Direction direction : directions) { - if (direction.getAxis().isHorizontal()) { - Direction direction2 = direction.getOpposite(); - blockState = blockState.setValue(FACING, direction2); - if (blockState.canSurvive(worldView, blockPos)) { - return blockState; - } - } - } - return null; - } - - @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 - @SuppressWarnings("deprecation") - public BlockState rotate(BlockState state, Rotation rotation) { - return BlocksHelper.rotateHorizontal(state, rotation, FACING); - } - - @Override - @SuppressWarnings("deprecation") - public BlockState mirror(BlockState state, Mirror mirror) { - return BlocksHelper.mirrorHorizontal(state, mirror, FACING); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseWeightedPlateBlock.java b/src/main/java/ru/bclib/blocks/BaseWeightedPlateBlock.java deleted file mode 100644 index 4e4b9d99..00000000 --- a/src/main/java/ru/bclib/blocks/BaseWeightedPlateBlock.java +++ /dev/null @@ -1,71 +0,0 @@ -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.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.WeightedPressurePlateBlock; -import net.minecraft.world.level.block.state.BlockState; -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.interfaces.BlockModelProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class BaseWeightedPlateBlock extends WeightedPressurePlateBlock implements BlockModelProvider { - private final Block parent; - - public BaseWeightedPlateBlock(Block source) { - super( - 15, - FabricBlockSettings.copyOf(source).noCollission().noOcclusion().requiresCorrectToolForDrops().strength(0.5F) - ); - this.parent = source; - } - - @Override - @SuppressWarnings("deprecation") - public List getDrops(BlockState state, LootContext.Builder builder) { - return Collections.singletonList(new ItemStack(this)); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return getBlockModel(resourceLocation, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - ResourceLocation parentId = Registry.BLOCK.getKey(parent); - Optional pattern; - if (blockState.getValue(POWER) > 0) { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_DOWN, parentId); - } - else { - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_PLATE_UP, parentId); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - String state = blockState.getValue(POWER) > 0 ? "_down" : "_up"; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createBlockSimple(modelId); - } -} diff --git a/src/main/java/ru/bclib/blocks/BaseWoodenButtonBlock.java b/src/main/java/ru/bclib/blocks/BaseWoodenButtonBlock.java deleted file mode 100644 index d6f34264..00000000 --- a/src/main/java/ru/bclib/blocks/BaseWoodenButtonBlock.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.world.level.block.Block; - -public class BaseWoodenButtonBlock extends BaseButtonBlock { - public BaseWoodenButtonBlock(Block source) { - super(source, FabricBlockSettings.copyOf(source).strength(0.5F, 0.5F).noOcclusion(), true); - } - - @Override - protected SoundEvent getSound(boolean clicked) { - return clicked ? SoundEvents.WOODEN_BUTTON_CLICK_ON : SoundEvents.WOODEN_BUTTON_CLICK_OFF; - } -} diff --git a/src/main/java/ru/bclib/blocks/BlockProperties.java b/src/main/java/ru/bclib/blocks/BlockProperties.java deleted file mode 100644 index 15a16c0f..00000000 --- a/src/main/java/ru/bclib/blocks/BlockProperties.java +++ /dev/null @@ -1,76 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.util.StringRepresentable; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.EnumProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; - -public class BlockProperties { - public static final EnumProperty TRIPLE_SHAPE = EnumProperty.create("shape", TripleShape.class); - public static final EnumProperty PENTA_SHAPE = EnumProperty.create("shape", PentaShape.class); - - public static final BooleanProperty TRANSITION = BooleanProperty.create("transition"); - public static final BooleanProperty HAS_LIGHT = BooleanProperty.create("has_light"); - public static final BooleanProperty IS_FLOOR = BooleanProperty.create("is_floor"); - public static final BooleanProperty NATURAL = BooleanProperty.create("natural"); - public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); - public static final BooleanProperty SMALL = BooleanProperty.create("small"); - - public static final IntegerProperty DEFAULT_ANVIL_DURABILITY = IntegerProperty.create("durability", 0, 3); - public static final IntegerProperty DESTRUCTION = IntegerProperty.create("destruction", 0, 2); - public static final IntegerProperty ROTATION = IntegerProperty.create("rotation", 0, 3); - public static final IntegerProperty FULLNESS = IntegerProperty.create("fullness", 0, 3); - public static final IntegerProperty COLOR = IntegerProperty.create("color", 0, 7); - public static final IntegerProperty SIZE = IntegerProperty.create("size", 0, 7); - public static final IntegerProperty AGE = IntegerProperty.create("age", 0, 3); - - public enum TripleShape implements StringRepresentable { - TOP("top", 0), MIDDLE("middle", 1), BOTTOM("bottom", 2); - - private final String name; - private final int index; - - TripleShape(String name, int index) { - this.name = name; - this.index = index; - } - - @Override - public String getSerializedName() { - return name; - } - - @Override - public String toString() { - return name; - } - - public int getIndex() { - return index; - } - - public static TripleShape fromIndex(int index) { - return index > 1 ? BOTTOM : index == 1 ? MIDDLE : TOP; - } - } - - public enum PentaShape implements StringRepresentable { - BOTTOM("bottom"), PRE_BOTTOM("pre_bottom"), MIDDLE("middle"), PRE_TOP("pre_top"), TOP("top"); - - private final String name; - - PentaShape(String name) { - this.name = name; - } - - @Override - public String getSerializedName() { - return name; - } - - @Override - public String toString() { - return name; - } - } -} diff --git a/src/main/java/ru/bclib/blocks/FeatureHangingSaplingBlock.java b/src/main/java/ru/bclib/blocks/FeatureHangingSaplingBlock.java deleted file mode 100644 index d75b5e07..00000000 --- a/src/main/java/ru/bclib/blocks/FeatureHangingSaplingBlock.java +++ /dev/null @@ -1,41 +0,0 @@ -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.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; - -import java.util.function.Function; - -public abstract class FeatureHangingSaplingBlock extends FeatureSaplingBlock { - private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12); - - public FeatureHangingSaplingBlock(Function> featureSupplier) { - super(featureSupplier); - } - - public FeatureHangingSaplingBlock(Function> featureSupplier, int light) { - super(light, featureSupplier); - } - - public FeatureHangingSaplingBlock(BlockBehaviour.Properties properties, Function> featureSupplier) { - super(properties, featureSupplier); - } - - @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; - } - -} diff --git a/src/main/java/ru/bclib/blocks/FeatureSaplingBlock.java b/src/main/java/ru/bclib/blocks/FeatureSaplingBlock.java deleted file mode 100644 index 0c7ed646..00000000 --- a/src/main/java/ru/bclib/blocks/FeatureSaplingBlock.java +++ /dev/null @@ -1,139 +0,0 @@ -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.BlockBehaviour; -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;import net.minecraft.util.RandomSource; -import java.util.function.Function; - -public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProvider, BlockModelProvider { - private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); - private final Function> feature; - - public FeatureSaplingBlock(Function> featureSupplier) { - this(FabricBlockSettings.of(Material.PLANT) - .collidable(false) - .instabreak() - .sound(SoundType.GRASS) - .randomTicks(), - featureSupplier - ); - } - - public FeatureSaplingBlock(int light, Function> featureSupplier) { - this(FabricBlockSettings.of(Material.PLANT) - .collidable(false) - .luminance(light) - .instabreak() - .sound(SoundType.GRASS) - .randomTicks(), - featureSupplier - ); - } - - public FeatureSaplingBlock(BlockBehaviour.Properties properties, Function> featureSupplier) { - super(null, properties); - this.feature = featureSupplier; - } - - protected Feature getFeature(BlockState state) { - return feature.apply(state); - } - - @Override - public List 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 level, RandomSource random, BlockPos pos, BlockState state) { - return random.nextInt(16) == 0; - } - - @Override - public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, RandomSource random) { - FeaturePlaceContext context = new FeaturePlaceContext( - Optional.empty(), - world, - world.getChunkSource().getGenerator(), - random, - pos, - null - ); - getFeature(blockState).place(context); - } - - @Override - public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - this.tick(state, world, pos, random); - } - - @Override - public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource 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 pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS, resourceLocation); - return ModelsHelper.fromPattern(pattern); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPE; - } -} diff --git a/src/main/java/ru/bclib/blocks/LeveledAnvilBlock.java b/src/main/java/ru/bclib/blocks/LeveledAnvilBlock.java deleted file mode 100644 index 8123f88b..00000000 --- a/src/main/java/ru/bclib/blocks/LeveledAnvilBlock.java +++ /dev/null @@ -1,16 +0,0 @@ -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; - } -} diff --git a/src/main/java/ru/bclib/blocks/SimpleLeavesBlock.java b/src/main/java/ru/bclib/blocks/SimpleLeavesBlock.java deleted file mode 100644 index f5cf201e..00000000 --- a/src/main/java/ru/bclib/blocks/SimpleLeavesBlock.java +++ /dev/null @@ -1,67 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; - -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.material.MaterialColor; -import ru.bclib.api.tag.NamedBlockTags; -import ru.bclib.api.tag.NamedItemTags; - -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.interfaces.TagProvider; -import ru.bclib.interfaces.tools.AddMineableHoe; -import ru.bclib.interfaces.tools.AddMineableShears; - -import java.util.List; - -public class SimpleLeavesBlock extends BaseBlockNotFull implements RenderLayerProvider, TagProvider, AddMineableShears, AddMineableHoe { - public SimpleLeavesBlock(MaterialColor color) { - this( - FabricBlockSettings - .of(Material.LEAVES) - .strength(0.2F) - .color(color) - .sound(SoundType.GRASS) - .noOcclusion() - .isValidSpawn((state, world, pos, type) -> false) - .isSuffocating((state, world, pos) -> false) - .isViewBlocking((state, world, pos) -> false) - ); - } - - public SimpleLeavesBlock(MaterialColor color, int light) { - this( - FabricBlockSettings - .of(Material.LEAVES) - .luminance(light) - .color(color) - .strength(0.2F) - .sound(SoundType.GRASS) - .noOcclusion() - .isValidSpawn((state, world, pos, type) -> false) - .isSuffocating((state, world, pos) -> false) - .isViewBlocking((state, world, pos) -> false) - ); - } - - public SimpleLeavesBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - public void addTags(List> blockTags, List> itemTags) { - blockTags.add(NamedBlockTags.LEAVES); - itemTags.add(NamedItemTags.LEAVES); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/StalactiteBlock.java b/src/main/java/ru/bclib/blocks/StalactiteBlock.java deleted file mode 100644 index 944b8b79..00000000 --- a/src/main/java/ru/bclib/blocks/StalactiteBlock.java +++ /dev/null @@ -1,257 +0,0 @@ -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.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.LiquidBlockContainer; -import net.minecraft.world.level.block.SimpleWaterloggedBlock; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -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.RenderLayerProvider; - -import java.util.Map; -import java.util.Optional; - -@SuppressWarnings("deprecation") -public class StalactiteBlock extends BaseBlockNotFull implements SimpleWaterloggedBlock, LiquidBlockContainer, RenderLayerProvider { - public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; - public static final BooleanProperty IS_FLOOR = BlockProperties.IS_FLOOR; - public static final IntegerProperty SIZE = BlockProperties.SIZE; - private static final VoxelShape[] SHAPES; - - public StalactiteBlock(Block source) { - this(FabricBlockSettings.copy(source).noOcclusion()); - } - - public StalactiteBlock(BlockBehaviour.Properties properties) { - super(properties); - this.registerDefaultState(getStateDefinition().any().setValue(SIZE, 0).setValue(IS_FLOOR, true).setValue(WATERLOGGED, false)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(WATERLOGGED, IS_FLOOR, SIZE); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPES[state.getValue(SIZE)]; - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext ctx) { - LevelReader world = ctx.getLevel(); - BlockPos pos = ctx.getClickedPos(); - Direction dir = ctx.getClickedFace(); - boolean water = world.getFluidState(pos).getType() == Fluids.WATER; - - if (dir == Direction.DOWN) { - if (isThis(world, pos.above()) || canSupportCenter(world, pos.above(), Direction.DOWN)) { - return defaultBlockState().setValue(IS_FLOOR, false).setValue(WATERLOGGED, water); - } - else if (isThis(world, pos.below()) || canSupportCenter(world, pos.below(), Direction.UP)) { - return defaultBlockState().setValue(IS_FLOOR, true).setValue(WATERLOGGED, water); - } - else { - return null; - } - } - else { - if (isThis(world, pos.below()) || canSupportCenter(world, pos.below(), Direction.UP)) { - return defaultBlockState().setValue(IS_FLOOR, true).setValue(WATERLOGGED, water); - } - else if (isThis(world, pos.above()) || canSupportCenter(world, pos.above(), Direction.DOWN)) { - return defaultBlockState().setValue(IS_FLOOR, false).setValue(WATERLOGGED, water); - } - else { - return null; - } - } - } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { - boolean hasUp = isThis(world, pos.above()); - boolean hasDown = isThis(world, pos.below()); - MutableBlockPos mut = new MutableBlockPos(); - if (hasUp && hasDown) { - boolean floor = state.getValue(IS_FLOOR); - BlockPos second = floor ? pos.above() : pos.below(); - BlockState bState = world.getBlockState(second); - world.setBlockAndUpdate(pos, state.setValue(SIZE, 1).setValue(IS_FLOOR, floor)); - world.setBlockAndUpdate(second, bState.setValue(SIZE, 1).setValue(IS_FLOOR, !floor)); - - bState = state; - int startSize = floor ? 1 : 2; - mut.set(pos.getX(), pos.getY() + 1, pos.getZ()); - for (int i = 0; i < 8 && isThis(bState); i++) { - world.setBlockAndUpdate(mut, bState.setValue(SIZE, startSize++).setValue(IS_FLOOR, false)); - mut.setY(mut.getY() + 1); - bState = world.getBlockState(mut); - } - - bState = state; - startSize = floor ? 2 : 1; - mut.set(pos.getX(), pos.getY() - 1, pos.getZ()); - for (int i = 0; i < 8 && isThis(bState); i++) { - world.setBlockAndUpdate(mut, bState.setValue(SIZE, startSize++).setValue(IS_FLOOR, true)); - mut.setY(mut.getY() - 1); - bState = world.getBlockState(mut); - } - } - else if (hasDown) { - mut.setX(pos.getX()); - mut.setZ(pos.getZ()); - for (int i = 1; i < 8; i++) { - mut.setY(pos.getY() - i); - if (isThis(world, mut)) { - BlockState state2 = world.getBlockState(mut); - int size = state2.getValue(SIZE); - if (size < i) { - world.setBlockAndUpdate(mut, state2.setValue(SIZE, i).setValue(IS_FLOOR, true)); - } - else { - break; - } - } - else { - break; - } - } - } - else if (hasUp) { - mut.setX(pos.getX()); - mut.setZ(pos.getZ()); - for (int i = 1; i < 8; i++) { - mut.setY(pos.getY() + i); - if (isThis(world, mut)) { - BlockState state2 = world.getBlockState(mut); - int size = state2.getValue(SIZE); - if (size < i) { - world.setBlockAndUpdate(mut, state2.setValue(SIZE, i).setValue(IS_FLOOR, false)); - } - else { - break; - } - } - else { - break; - } - } - } - } - - private boolean isThis(LevelReader world, BlockPos pos) { - return isThis(world.getBlockState(pos)); - } - - private boolean isThis(BlockState state) { - return state.getBlock() instanceof StalactiteBlock; - } - - @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(); - } - return state; - } - - @Override - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - int size = state.getValue(SIZE); - return checkUp(world, pos, size) || checkDown(world, pos, size); - } - - private boolean checkUp(BlockGetter world, BlockPos pos, int size) { - BlockPos p = pos.above(); - BlockState state = world.getBlockState(p); - return (isThis(state) && state.getValue(SIZE) >= size) || state.isCollisionShapeFullBlock(world, p); - } - - private boolean checkDown(BlockGetter world, BlockPos pos, int size) { - BlockPos p = pos.below(); - BlockState state = world.getBlockState(p); - return (isThis(state) && state.getValue(SIZE) >= size) || state.isCollisionShapeFullBlock(world, p); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_CROSS_SHADED, resourceLocation); - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - BlockModelRotation rotation = blockState.getValue(IS_FLOOR) ? BlockModelRotation.X0_Y0 : BlockModelRotation.X180_Y0; - ResourceLocation modelId = new ResourceLocation( - stateId.getNamespace(), - stateId.getPath() + "_" + blockState.getValue(SIZE) - ); - registerBlockModel(modelId, modelId, blockState, modelCache); - return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); - } - - @Override - public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { - return false; - } - - @Override - public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { - return false; - } - - @Override - public FluidState getFluidState(BlockState state) { - return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : Fluids.EMPTY.defaultFluidState(); - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - static { - float end = 2F / 8F; - float start = 5F / 8F; - SHAPES = new VoxelShape[8]; - for (int i = 0; i < 8; i++) { - int side = Mth.floor(Mth.lerp(i / 7F, start, end) * 8F + 0.5F); - SHAPES[i] = Block.box(side, 0, side, 16 - side, 16, 16 - side); - } - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/StonePressurePlateBlock.java b/src/main/java/ru/bclib/blocks/StonePressurePlateBlock.java deleted file mode 100644 index b1da28b9..00000000 --- a/src/main/java/ru/bclib/blocks/StonePressurePlateBlock.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.world.level.block.Block; - -public class StonePressurePlateBlock extends BasePressurePlateBlock { - public StonePressurePlateBlock(Block source) { - super(Sensitivity.MOBS, source); - } -} diff --git a/src/main/java/ru/bclib/blocks/StripableBarkBlock.java b/src/main/java/ru/bclib/blocks/StripableBarkBlock.java deleted file mode 100644 index d04bcf59..00000000 --- a/src/main/java/ru/bclib/blocks/StripableBarkBlock.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.RotatedPillarBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.phys.BlockHitResult; -import ru.bclib.api.tag.NamedMineableTags; -import ru.bclib.api.tag.TagAPI; - -public class StripableBarkBlock extends BaseBarkBlock { - private final Block striped; - - public StripableBarkBlock(MaterialColor color, Block striped) { - super(FabricBlockSettings.copyOf(striped).color(color)); - this.striped = striped; - } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - if (TagAPI.isToolWithMineableTag(player.getMainHandItem(), NamedMineableTags.AXE)){ - world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); - if (!world.isClientSide) { - world.setBlock(pos, - striped.defaultBlockState() - .setValue(RotatedPillarBlock.AXIS, state.getValue(RotatedPillarBlock.AXIS)), - 11 - ); - if (!player.isCreative()) { - player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); - } - } - return InteractionResult.SUCCESS; - } - return InteractionResult.FAIL; - } -} diff --git a/src/main/java/ru/bclib/blocks/TripleTerrainBlock.java b/src/main/java/ru/bclib/blocks/TripleTerrainBlock.java deleted file mode 100644 index b32e6ee0..00000000 --- a/src/main/java/ru/bclib/blocks/TripleTerrainBlock.java +++ /dev/null @@ -1,174 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.MultiVariant; -import net.minecraft.client.renderer.block.model.Variant; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.UnbakedModel; -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.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.EnumProperty; -import net.minecraft.world.level.material.MaterialColor; -import net.minecraft.world.phys.BlockHitResult; -import org.jetbrains.annotations.Nullable; -import ru.bclib.blocks.BlockProperties.TripleShape; -import ru.bclib.client.models.BasePatterns; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Random;import net.minecraft.util.RandomSource; - -public class TripleTerrainBlock extends BaseTerrainBlock { - public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; - - public TripleTerrainBlock(Block baseBlock) { - super(baseBlock, baseBlock.defaultMaterialColor()); - this.registerDefaultState(defaultBlockState().setValue(SHAPE, TripleShape.BOTTOM)); - } - - public TripleTerrainBlock(Block baseBlock, MaterialColor color) { - super(baseBlock, color); - this.registerDefaultState(defaultBlockState().setValue(SHAPE, TripleShape.BOTTOM)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(SHAPE); - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext ctx) { - Direction dir = ctx.getClickedFace(); - TripleShape shape = dir == Direction.UP ? TripleShape.BOTTOM : dir == Direction.DOWN ? TripleShape.TOP : TripleShape.MIDDLE; - return defaultBlockState().setValue(SHAPE, shape); - } - - @Override - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - TripleShape shape = state.getValue(SHAPE); - if (shape == TripleShape.BOTTOM) { - return super.use(state, world, pos, player, hand, hit); - } - return InteractionResult.FAIL; - } - - @Override - public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - TripleShape shape = state.getValue(SHAPE); - if (shape == TripleShape.BOTTOM) { - super.randomTick(state, world, pos, random); - } - else if (random.nextInt(16) == 0) { - boolean bottom = canStayBottom(world, pos); - if (shape == TripleShape.TOP) { - if (!bottom) { - world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); - } - } - else { - boolean top = canStay(state, world, pos) || isMiddle(world.getBlockState(pos.above())); - if (!top && !bottom) { - world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); - } - else if (top && !bottom) { - world.setBlockAndUpdate(pos, state.setValue(SHAPE, TripleShape.BOTTOM)); - } - else if (!top) { - world.setBlockAndUpdate(pos, state.setValue(SHAPE, TripleShape.TOP)); - } - } - } - } - - protected boolean canStayBottom(LevelReader world, BlockPos pos) { - BlockPos blockPos = pos.below(); - BlockState blockState = world.getBlockState(blockPos); - if (isMiddle(blockState)) { - return true; - } - else if (blockState.getFluidState().getAmount() == 8) { - return false; - } - else { - return !blockState.isFaceSturdy(world, blockPos, Direction.UP); - } - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation blockId) { - return getBlockModel(blockId, defaultBlockState()); - } - - @Override - @Environment(EnvType.CLIENT) - public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { - String path = blockId.getPath(); - Optional pattern; - if (isMiddle(blockState)) { - ResourceLocation topId = new ResourceLocation(blockId.getNamespace(), path + "_top"); - pattern = PatternsHelper.createBlockSimple(topId); - } - else { - Map textures = Maps.newHashMap(); - textures.put("%top%", "betterend:block/" + path + "_top"); - textures.put("%side%", "betterend:block/" + path + "_side"); - textures.put("%bottom%", "minecraft:block/end_stone"); - pattern = PatternsHelper.createJson(BasePatterns.BLOCK_TOP_SIDE_BOTTOM, textures); - } - return ModelsHelper.fromPattern(pattern); - } - - @Override - @Environment(EnvType.CLIENT) - public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - boolean isMiddle = isMiddle(blockState); - String middle = isMiddle ? "_middle" : ""; - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + middle); - registerBlockModel(stateId, modelId, blockState, modelCache); - if (isMiddle) { - List variants = Lists.newArrayList(); - for (BlockModelRotation rotation : BlockModelRotation.values()) { - variants.add(new Variant(modelId, rotation.getRotation(), false, 1)); - } - return new MultiVariant(variants); - } - else if (blockState.getValue(SHAPE) == TripleShape.TOP) { - return new MultiVariant(Lists.newArrayList( - new Variant( - modelId, - BlockModelRotation.X180_Y0.getRotation(), - false, - 1 - ), - new Variant(modelId, BlockModelRotation.X180_Y90.getRotation(), false, 1), - new Variant(modelId, BlockModelRotation.X180_Y180.getRotation(), false, 1), - new Variant(modelId, BlockModelRotation.X180_Y270.getRotation(), false, 1) - )); - } - return ModelsHelper.createRandomTopModel(modelId); - } - - protected boolean isMiddle(BlockState blockState) { - return blockState.is(this) && blockState.getValue(SHAPE) == TripleShape.MIDDLE; - } -} diff --git a/src/main/java/ru/bclib/blocks/UnderwaterPlantBlock.java b/src/main/java/ru/bclib/blocks/UnderwaterPlantBlock.java deleted file mode 100644 index 00b87105..00000000 --- a/src/main/java/ru/bclib/blocks/UnderwaterPlantBlock.java +++ /dev/null @@ -1,162 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.RandomSource; -import net.minecraft.world.entity.item.ItemEntity; -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.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.BonemealableBlock; -import net.minecraft.world.level.block.LiquidBlockContainer; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.items.tool.BaseShearsItem; - -import java.util.List; -import java.util.Random; -import java.util.function.Function; - -import net.minecraft.util.RandomSource; - -public abstract class UnderwaterPlantBlock extends BaseBlockNotFull implements RenderLayerProvider, BonemealableBlock, LiquidBlockContainer { - private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); - - public UnderwaterPlantBlock(){this(p->p);} - public UnderwaterPlantBlock(Function propMod) { - this( - propMod.apply(FabricBlockSettings - .of(Material.WATER_PLANT) - .sound(SoundType.WET_GRASS) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ)) - ); - } - - public UnderwaterPlantBlock(int light) { - this(light, p->p); - } - - public UnderwaterPlantBlock(int light, Function propMod) { - this( - propMod.apply(FabricBlockSettings - .of(Material.WATER_PLANT) - .luminance(light) - .sound(SoundType.WET_GRASS) - .noCollission() - .offsetType(BlockBehaviour.OffsetType.XZ)) - ); - } - - public UnderwaterPlantBlock(Properties settings) { - super(settings); - } - - @Override - @SuppressWarnings("deprecation") - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - Vec3 vec3d = state.getOffset(view, pos); - return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); - } - - @Override - @SuppressWarnings("deprecation") - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - BlockState down = world.getBlockState(pos.below()); - state = world.getBlockState(pos); - return isTerrain(down) && state.getFluidState().getType().equals(Fluids.WATER.getSource()); - } - - protected abstract boolean isTerrain(BlockState state); - - @Override - @SuppressWarnings("deprecation") - public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { - if (!canSurvive(state, world, pos)) { - world.destroyBlock(pos, true); - return Blocks.WATER.defaultBlockState(); - } - else { - return state; - } - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( - Enchantments.SILK_TOUCH, - tool - ) > 0) { - return Lists.newArrayList(new ItemStack(this)); - } - else { - return Lists.newArrayList(); - } - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { - return true; - } - - @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { - return true; - } - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - ItemEntity item = new ItemEntity( - level, - pos.getX() + 0.5, - pos.getY() + 0.5, - pos.getZ() + 0.5, - new ItemStack(this) - ); - level.addFreshEntity(item); - } - - @Override - public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { - return false; - } - - @Override - public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { - return false; - } - - @Override - @SuppressWarnings("deprecation") - public FluidState getFluidState(BlockState state) { - return Fluids.WATER.getSource(false); - } - -} diff --git a/src/main/java/ru/bclib/blocks/UnderwaterPlantWithAgeBlock.java b/src/main/java/ru/bclib/blocks/UnderwaterPlantWithAgeBlock.java deleted file mode 100644 index e620d272..00000000 --- a/src/main/java/ru/bclib/blocks/UnderwaterPlantWithAgeBlock.java +++ /dev/null @@ -1,57 +0,0 @@ -package ru.bclib.blocks; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.material.Material; - -import java.util.Random;import net.minecraft.util.RandomSource; - -public abstract class UnderwaterPlantWithAgeBlock extends UnderwaterPlantBlock { - public static final IntegerProperty AGE = BlockProperties.AGE; - - public UnderwaterPlantWithAgeBlock() { - super( - FabricBlockSettings - .of(Material.WATER_PLANT) - .sound(SoundType.WET_GRASS) - .randomTicks() - .noCollission() - ); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { - stateManager.add(AGE); - } - - public abstract void grow(WorldGenLevel world, RandomSource random, BlockPos pos); - - @Override - public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { - if (random.nextInt(4) == 0) { - int age = state.getValue(AGE); - if (age < 3) { - world.setBlockAndUpdate(pos, state.setValue(AGE, age + 1)); - } - else { - grow(world, random, pos); - } - } - } - - @Override - @SuppressWarnings("deprecation") - public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - super.tick(state, world, pos, random); - if (isBonemealSuccess(world, random, pos, state)) { - performBonemeal(world, random, pos, state); - } - } -} diff --git a/src/main/java/ru/bclib/blocks/UpDownPlantBlock.java b/src/main/java/ru/bclib/blocks/UpDownPlantBlock.java deleted file mode 100644 index fe9a8b18..00000000 --- a/src/main/java/ru/bclib/blocks/UpDownPlantBlock.java +++ /dev/null @@ -1,104 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.entity.player.Player; -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.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; -import ru.bclib.client.render.BCLRenderLayer; -import ru.bclib.interfaces.RenderLayerProvider; -import ru.bclib.interfaces.tools.AddMineableHoe; -import ru.bclib.interfaces.tools.AddMineableShears; -import ru.bclib.items.tool.BaseShearsItem; - -import java.util.List; - -public abstract class UpDownPlantBlock extends BaseBlockNotFull implements RenderLayerProvider, AddMineableShears, AddMineableHoe { - private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 16, 12); - - public UpDownPlantBlock() { - this(FabricBlockSettings - .of(Material.PLANT) - .sound(SoundType.GRASS) - .noCollission() - ); - } - - public UpDownPlantBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - protected abstract boolean isTerrain(BlockState state); - - @Override - @SuppressWarnings("deprecation") - public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { - return SHAPE; - } - - @Override - @SuppressWarnings("deprecation") - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - BlockState down = world.getBlockState(pos.below()); - BlockState up = world.getBlockState(pos.above()); - return (isTerrain(down) || down.getBlock() == this) && (isSupport(up, world, pos) || up.getBlock() == this); - } - - protected boolean isSupport(BlockState state, LevelReader world, BlockPos pos) { - return canSupportCenter(world, pos.above(), Direction.UP); - } - - @Override - @SuppressWarnings("deprecation") - 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 List getDrops(BlockState state, LootContext.Builder builder) { - ItemStack tool = builder.getParameter(LootContextParams.TOOL); - if (tool != null && BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel( - Enchantments.SILK_TOUCH, - tool - ) > 0) { - return Lists.newArrayList(new ItemStack(this)); - } - else { - return Lists.newArrayList(); - } - } - - @Override - public BCLRenderLayer getRenderLayer() { - return BCLRenderLayer.CUTOUT; - } - - @Override - public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack stack) { - super.playerDestroy(world, player, pos, state, blockEntity, stack); - world.neighborChanged(pos, Blocks.AIR, pos.below()); - } -} diff --git a/src/main/java/ru/bclib/blocks/WallMushroomBlock.java b/src/main/java/ru/bclib/blocks/WallMushroomBlock.java deleted file mode 100644 index ff6e6c5a..00000000 --- a/src/main/java/ru/bclib/blocks/WallMushroomBlock.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.bclib.blocks; - -import com.google.common.collect.Lists; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.storage.loot.LootContext; - -import java.util.List; - -public abstract class WallMushroomBlock extends BaseWallPlantBlock { - public WallMushroomBlock(int light) { - this( - FabricBlockSettings - .of(Material.PLANT) - .luminance(light) - .destroyTime(0.2F) - .sound(SoundType.GRASS) - .sound(SoundType.WOOD) - .noCollission() - ); - } - - public WallMushroomBlock(BlockBehaviour.Properties properties) { - super(properties); - } - - @Override - public List getDrops(BlockState state, LootContext.Builder builder) { - return Lists.newArrayList(new ItemStack(this)); - } - - @Override - public boolean isSupport(LevelReader world, BlockPos pos, BlockState blockState, Direction direction) { - return blockState.getMaterial().isSolid() && blockState.isFaceSturdy(world, pos, direction); - } -} diff --git a/src/main/java/ru/bclib/blocks/WoodenPressurePlateBlock.java b/src/main/java/ru/bclib/blocks/WoodenPressurePlateBlock.java deleted file mode 100644 index 03b2a860..00000000 --- a/src/main/java/ru/bclib/blocks/WoodenPressurePlateBlock.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.blocks; - -import net.minecraft.world.level.block.Block; - -public class WoodenPressurePlateBlock extends BasePressurePlateBlock { - public WoodenPressurePlateBlock(Block source) { - super(Sensitivity.EVERYTHING, source); - } -} diff --git a/src/main/java/ru/bclib/client/BCLibClient.java b/src/main/java/ru/bclib/client/BCLibClient.java deleted file mode 100644 index bb61f255..00000000 --- a/src/main/java/ru/bclib/client/BCLibClient.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.bclib.client; - -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; -import net.fabricmc.fabric.api.client.model.ModelProviderContext; -import net.fabricmc.fabric.api.client.model.ModelProviderException; -import net.fabricmc.fabric.api.client.model.ModelResourceProvider; -import net.fabricmc.fabric.api.client.model.ModelVariantProvider; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; -import ru.bclib.api.ModIntegrationAPI; -import ru.bclib.api.PostInitAPI; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.client.models.CustomModelBakery; -import ru.bclib.registry.BaseBlockEntityRenders; - -public class BCLibClient implements ClientModInitializer, ModelResourceProvider, ModelVariantProvider { - public static CustomModelBakery modelBakery; - - @Override - public void onInitializeClient() { - ModIntegrationAPI.registerAll(); - BaseBlockEntityRenders.register(); - DataExchangeAPI.prepareClientside(); - PostInitAPI.postInit(true); - modelBakery = new CustomModelBakery(); - ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> this); - ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> this); - } - - @Override - public @Nullable UnbakedModel loadModelResource(ResourceLocation resourceId, ModelProviderContext context) throws ModelProviderException { - return modelBakery.getBlockModel(resourceId); - } - - @Override - public @Nullable UnbakedModel loadModelVariant(ModelResourceLocation modelId, ModelProviderContext context) throws ModelProviderException { - return modelId.getVariant().equals("inventory") ? modelBakery.getItemModel( modelId) : modelBakery.getBlockModel(modelId); - } -} diff --git a/src/main/java/ru/bclib/client/models/BaseChestBlockModel.java b/src/main/java/ru/bclib/client/models/BaseChestBlockModel.java deleted file mode 100644 index 745e12f0..00000000 --- a/src/main/java/ru/bclib/client/models/BaseChestBlockModel.java +++ /dev/null @@ -1,108 +0,0 @@ -package ru.bclib.client.models; - -import net.minecraft.client.model.geom.ModelPart; -import net.minecraft.client.model.geom.PartPose; -import net.minecraft.client.model.geom.builders.CubeDeformation; -import net.minecraft.client.model.geom.builders.CubeListBuilder; -import net.minecraft.client.model.geom.builders.LayerDefinition; -import net.minecraft.client.model.geom.builders.MeshDefinition; -import net.minecraft.client.model.geom.builders.PartDefinition; - -public class BaseChestBlockModel { - public final ModelPart partA; - public final ModelPart partC; - public final ModelPart partB; - public final ModelPart partRightA; - public final ModelPart partRightC; - public final ModelPart partRightB; - public final ModelPart partLeftA; - public final ModelPart partLeftC; - public final ModelPart partLeftB; - - public static LayerDefinition getTexturedModelData() { - MeshDefinition modelData = new MeshDefinition(); - PartDefinition modelPartData = modelData.getRoot(); - CubeDeformation deformation_partC = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partC", - CubeListBuilder.create().texOffs(0, 19).addBox(1.0f, 0.0f, 1.0f, 14.0f, 9.0f, 14.0f, deformation_partC), - PartPose.ZERO - ); - - CubeDeformation deformation_partA = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partA", - CubeListBuilder.create().texOffs(0, 0).addBox(1.0f, 0.0f, 0.0f, 14.0f, 5.0f, 14.0f, deformation_partA), - PartPose.offset(0.0f, 9.0f, 1.0f) - ); - - CubeDeformation deformation_partB = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partB", - CubeListBuilder.create().texOffs(0, 0).addBox(7.0f, -1.0f, 15.0f, 2.0f, 4.0f, 1.0f, deformation_partB), - PartPose.offset(0.0f, 8.0f, 0.0f) - ); - - CubeDeformation deformation_partRightC = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partRightC", - CubeListBuilder.create() - .texOffs(0, 19) - .addBox(1.0f, 0.0f, 1.0f, 15.0f, 9.0f, 14.0f, deformation_partRightC), - PartPose.ZERO - ); - - CubeDeformation deformation_partRightA = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partRightA", - CubeListBuilder.create().texOffs(0, 0).addBox(1.0f, 0.0f, 0.0f, 15.0f, 5.0f, 14.0f, deformation_partRightA), - PartPose.offset(0.0f, 9.0f, 1.0f) - ); - - CubeDeformation deformation_partRightB = new CubeDeformation(0.0f); - PartDefinition partRightB = modelPartData.addOrReplaceChild( - "partRightB", - CubeListBuilder.create() - .texOffs(0, 0) - .addBox(15.0f, -1.0f, 15.0f, 1.0f, 4.0f, 1.0f, deformation_partRightB), - PartPose.offset(0.0f, 8.0f, 0.0f) - ); - - CubeDeformation deformation_partLeftC = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partLeftC", - CubeListBuilder.create().texOffs(0, 19).addBox(0.0f, 0.0f, 1.0f, 15.0f, 9.0f, 14.0f, deformation_partLeftC), - PartPose.ZERO - ); - - CubeDeformation deformation_partLeftA = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partLeftA", - CubeListBuilder.create().texOffs(0, 0).addBox(0.0f, 0.0f, 0.0f, 15.0f, 5.0f, 14.0f, deformation_partLeftA), - PartPose.offset(0.0f, 9.0f, 1.0f) - ); - - CubeDeformation deformation_partLeftB = new CubeDeformation(0.0f); - modelPartData.addOrReplaceChild( - "partLeftB", - CubeListBuilder.create().texOffs(0, 0).addBox(0.0f, -1.0f, 15.0f, 1.0f, 4.0f, 1.0f, deformation_partLeftB), - PartPose.offset(0.0f, 8.0f, 0.0f) - ); - - return LayerDefinition.create(modelData, 64, 64); - } - - public BaseChestBlockModel(ModelPart modelPart) { - super(); - - partC = modelPart.getChild("partC"); - partA = modelPart.getChild("partA"); - partB = modelPart.getChild("partB"); - partRightC = modelPart.getChild("partRightC"); - partRightA = modelPart.getChild("partRightA"); - partRightB = modelPart.getChild("partRightB"); - partLeftC = modelPart.getChild("partLeftC"); - partLeftA = modelPart.getChild("partLeftA"); - partLeftB = modelPart.getChild("partLeftB"); - } -} diff --git a/src/main/java/ru/bclib/client/models/BasePatterns.java b/src/main/java/ru/bclib/client/models/BasePatterns.java deleted file mode 100644 index 5d3442a8..00000000 --- a/src/main/java/ru/bclib/client/models/BasePatterns.java +++ /dev/null @@ -1,61 +0,0 @@ -package ru.bclib.client.models; - -import net.minecraft.resources.ResourceLocation; -import ru.bclib.BCLib; - -public class BasePatterns { - //Block Models - public final static ResourceLocation BLOCK_EMPTY = BCLib.makeID("patterns/block/empty.json"); - public final static ResourceLocation BLOCK_BASE = BCLib.makeID("patterns/block/block.json"); - public final static ResourceLocation BLOCK_SIDED = BCLib.makeID("patterns/block/block_sided.json"); - public final static ResourceLocation BLOCK_BOTTOM_TOP = BCLib.makeID("patterns/block/block_bottom_top.json"); - public final static ResourceLocation BLOCK_SLAB = BCLib.makeID("patterns/block/slab.json"); - public final static ResourceLocation BLOCK_STAIR = BCLib.makeID("patterns/block/stairs.json"); - public final static ResourceLocation BLOCK_STAIR_INNER = BCLib.makeID("patterns/block/stairs_inner.json"); - public final static ResourceLocation BLOCK_STAIR_OUTER = BCLib.makeID("patterns/block/stairs_outer.json"); - public final static ResourceLocation BLOCK_WALL_POST = BCLib.makeID("patterns/block/wall_post.json"); - public final static ResourceLocation BLOCK_WALL_SIDE = BCLib.makeID("patterns/block/wall_side.json"); - public final static ResourceLocation BLOCK_WALL_SIDE_TALL = BCLib.makeID("patterns/block/wall_side_tall.json"); - public final static ResourceLocation BLOCK_FENCE_POST = BCLib.makeID("patterns/block/fence_post.json"); - public final static ResourceLocation BLOCK_FENCE_SIDE = BCLib.makeID("patterns/block/fence_side.json"); - public final static ResourceLocation BLOCK_BUTTON = BCLib.makeID("patterns/block/button.json"); - public final static ResourceLocation BLOCK_BUTTON_PRESSED = BCLib.makeID("patterns/block/button_pressed.json"); - public final static ResourceLocation BLOCK_PILLAR = BCLib.makeID("patterns/block/pillar.json"); - public final static ResourceLocation BLOCK_PLATE_UP = BCLib.makeID("patterns/block/pressure_plate_up.json"); - public final static ResourceLocation BLOCK_PLATE_DOWN = BCLib.makeID("patterns/block/pressure_plate_down.json"); - public final static ResourceLocation BLOCK_DOOR_TOP = BCLib.makeID("patterns/block/door_top.json"); - public final static ResourceLocation BLOCK_DOOR_TOP_HINGE = BCLib.makeID("patterns/block/door_top_hinge.json"); - public final static ResourceLocation BLOCK_DOOR_BOTTOM = BCLib.makeID("patterns/block/door_bottom.json"); - public final static ResourceLocation BLOCK_DOOR_BOTTOM_HINGE = BCLib.makeID("patterns/block/door_bottom_hinge.json"); - public final static ResourceLocation BLOCK_CROSS = BCLib.makeID("patterns/block/cross.json"); - public final static ResourceLocation BLOCK_CROSS_SHADED = BCLib.makeID("patterns/block/cross_shaded.json"); - public final static ResourceLocation BLOCK_GATE_CLOSED = BCLib.makeID("patterns/block/fence_gate_closed.json"); - public final static ResourceLocation BLOCK_GATE_CLOSED_WALL = BCLib.makeID("patterns/block/wall_gate_closed.json"); - public final static ResourceLocation BLOCK_GATE_OPEN = BCLib.makeID("patterns/block/fence_gate_open.json"); - public final static ResourceLocation BLOCK_GATE_OPEN_WALL = BCLib.makeID("patterns/block/wall_gate_open.json"); - public final static ResourceLocation BLOCK_TRAPDOOR = BCLib.makeID("patterns/block/trapdoor.json"); - public final static ResourceLocation BLOCK_LADDER = BCLib.makeID("patterns/block/ladder.json"); - public final static ResourceLocation BLOCK_BARREL_OPEN = BCLib.makeID("patterns/block/barrel_open.json"); - public final static ResourceLocation BLOCK_BOOKSHELF = BCLib.makeID("patterns/block/bookshelf.json"); - public final static ResourceLocation BLOCK_COMPOSTER = BCLib.makeID("patterns/block/composter.json"); - public final static ResourceLocation BLOCK_COLORED = BCLib.makeID("patterns/block/block_colored.json"); - public final static ResourceLocation BLOCK_BARS_POST = BCLib.makeID("patterns/block/bars_post.json"); - public final static ResourceLocation BLOCK_BARS_SIDE = BCLib.makeID("patterns/block/bars_side.json"); - public final static ResourceLocation BLOCK_ANVIL = BCLib.makeID("patterns/block/anvil.json"); - public final static ResourceLocation BLOCK_CHAIN = BCLib.makeID("patterns/block/chain.json"); - public final static ResourceLocation BLOCK_FURNACE = BCLib.makeID("patterns/block/furnace.json"); - public final static ResourceLocation BLOCK_FURNACE_LIT = BCLib.makeID("patterns/block/furnace_glow.json"); - public final static ResourceLocation BLOCK_TOP_SIDE_BOTTOM = BCLib.makeID("patterns/block/top_side_bottom.json"); - public final static ResourceLocation BLOCK_PATH = BCLib.makeID("patterns/block/path.json"); - - //Item Models - public final static ResourceLocation ITEM_WALL = BCLib.makeID("patterns/item/pattern_wall.json"); - public final static ResourceLocation ITEM_FENCE = BCLib.makeID("patterns/item/pattern_fence.json"); - public final static ResourceLocation ITEM_BUTTON = BCLib.makeID("patterns/item/pattern_button.json"); - public final static ResourceLocation ITEM_CHEST = BCLib.makeID("patterns/item/pattern_chest.json"); - public final static ResourceLocation ITEM_BLOCK = BCLib.makeID("patterns/item/pattern_block_item.json"); - public final static ResourceLocation ITEM_GENERATED = BCLib.makeID("patterns/item/pattern_item_generated.json"); - public final static ResourceLocation ITEM_HANDHELD = BCLib.makeID("patterns/item/pattern_item_handheld.json"); - public final static ResourceLocation ITEM_SPAWN_EGG = BCLib.makeID("patterns/item/pattern_item_spawn_egg.json"); - -} diff --git a/src/main/java/ru/bclib/client/models/CustomModelBakery.java b/src/main/java/ru/bclib/client/models/CustomModelBakery.java deleted file mode 100644 index 86edc933..00000000 --- a/src/main/java/ru/bclib/client/models/CustomModelBakery.java +++ /dev/null @@ -1,132 +0,0 @@ -package ru.bclib.client.models; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.mojang.datafixers.util.Pair; -import net.minecraft.client.renderer.block.BlockModelShaper; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.multipart.MultiPart; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.api.ModIntegrationAPI; -import ru.bclib.client.render.EmissiveTextureInfo; -import ru.bclib.interfaces.BlockModelProvider; -import ru.bclib.interfaces.ItemModelProvider; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class CustomModelBakery { - private final Map models = Maps.newConcurrentMap(); - - public UnbakedModel getBlockModel(ResourceLocation location) { - return models.get(location); - } - - public UnbakedModel getItemModel(ResourceLocation location) { - ResourceLocation storageID = new ResourceLocation(location.getNamespace(), "models/item/" + location.getPath() + ".json"); - return models.get(location); - } - - public void loadCustomModels(ResourceManager resourceManager) { - Registry.BLOCK.stream().parallel().filter(block -> block instanceof BlockModelProvider).forEach(block -> { - ResourceLocation blockID = Registry.BLOCK.getKey(block); - ResourceLocation storageID = new ResourceLocation(blockID.getNamespace(), "blockstates/" + blockID.getPath() + ".json"); - if (resourceManager.getResource(storageID).isEmpty()) { - addBlockModel(blockID, block); - } - storageID = new ResourceLocation(blockID.getNamespace(), "models/item/" + blockID.getPath() + ".json"); - if (resourceManager.getResource(storageID).isEmpty()) { - addItemModel(blockID, (ItemModelProvider) block); - } - }); - - Registry.ITEM.stream().parallel().filter(item -> item instanceof ItemModelProvider).forEach(item -> { - ResourceLocation registryID = Registry.ITEM.getKey(item); - ResourceLocation storageID = new ResourceLocation(registryID.getNamespace(), "models/item/" + registryID.getPath() + ".json"); - if (resourceManager.getResource(storageID).isEmpty()) { - addItemModel(registryID, (ItemModelProvider) item); - } - }); - } - - private void addBlockModel(ResourceLocation blockID, Block block) { - BlockModelProvider provider = (BlockModelProvider) block; - ImmutableList states = block.getStateDefinition().getPossibleStates(); - BlockState defaultState = block.defaultBlockState(); - - ResourceLocation defaultStateID = BlockModelShaper.stateToModelLocation(blockID, defaultState); - UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, models); - - if (defaultModel instanceof MultiPart) { - states.forEach(blockState -> { - ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); - models.put(stateID, defaultModel); - }); - } - else { - states.forEach(blockState -> { - ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); - UnbakedModel model = stateID.equals(defaultStateID) ? defaultModel : provider.getModelVariant(stateID, blockState, models); - models.put(stateID, model); - }); - } - } - - private void addItemModel(ResourceLocation itemID, ItemModelProvider provider) { - ModelResourceLocation modelLocation = new ModelResourceLocation(itemID.getNamespace(), itemID.getPath(), "inventory"); - if (models.containsKey(modelLocation)) { - return; - } - BlockModel model = provider.getItemModel(modelLocation); - models.put(modelLocation, model); - } - - public static void loadEmissiveModels(Map unbakedCache) { - if (!ModIntegrationAPI.hasCanvas()) { - return; - } - - Map cacheCopy = new HashMap<>(unbakedCache); - Set> strings = Sets.newConcurrentHashSet(); - Registry.BLOCK.keySet().forEach(blockID -> { - Block block = Registry.BLOCK.get(blockID); - ImmutableList states = block.getStateDefinition().getPossibleStates(); - boolean addBlock = false; - - for (BlockState state: states) { - ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, state); - UnbakedModel model = cacheCopy.get(stateID); - if (model == null) { - continue; - } - Collection materials = model.getMaterials(cacheCopy::get, strings); - if (materials == null) { - continue; - } - for (Material material: materials) { - if (EmissiveTextureInfo.isEmissiveTexture(material.texture())) { - addBlock = true; - break; - } - } - if (addBlock) { - break; - } - } - - if (addBlock) { - EmissiveTextureInfo.addBlock(blockID); - } - }); - } -} diff --git a/src/main/java/ru/bclib/client/models/CustomModelData.java b/src/main/java/ru/bclib/client/models/CustomModelData.java deleted file mode 100644 index 0fcf50f9..00000000 --- a/src/main/java/ru/bclib/client/models/CustomModelData.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.bclib.client.models; - -import com.google.common.collect.Sets; -import net.minecraft.resources.ResourceLocation; - -import java.util.Set; - -public class CustomModelData { - private static final Set TRANSPARENT_EMISSION = Sets.newConcurrentHashSet(); - - public static void clear() { - TRANSPARENT_EMISSION.clear(); - } - - public static void addTransparent(ResourceLocation blockID) { - TRANSPARENT_EMISSION.add(blockID); - } - - public static boolean isTransparentEmissive(ResourceLocation rawLocation) { - String name = rawLocation.getPath().replace("materialmaps/block/", "").replace(".json", ""); - return TRANSPARENT_EMISSION.contains(new ResourceLocation(rawLocation.getNamespace(), name)); - } -} diff --git a/src/main/java/ru/bclib/client/models/ModelsHelper.java b/src/main/java/ru/bclib/client/models/ModelsHelper.java deleted file mode 100644 index 8b99b720..00000000 --- a/src/main/java/ru/bclib/client/models/ModelsHelper.java +++ /dev/null @@ -1,155 +0,0 @@ -package ru.bclib.client.models; - -import com.google.common.collect.Lists; -import com.mojang.math.Transformation; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.MultiVariant; -import net.minecraft.client.renderer.block.model.Variant; -import net.minecraft.client.renderer.block.model.multipart.Condition; -import net.minecraft.client.renderer.block.model.multipart.MultiPart; -import net.minecraft.client.renderer.block.model.multipart.Selector; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; - -import java.util.List; -import java.util.Optional; -import java.util.function.Function; - -@Environment(EnvType.CLIENT) -public class ModelsHelper { - public static BlockModel fromPattern(Optional pattern) { - return pattern.map(BlockModel::fromString).orElse(null); - } - - public static BlockModel createItemModel(ResourceLocation resourceLocation) { - return fromPattern(PatternsHelper.createItemGenerated(resourceLocation)); - } - - public static BlockModel createHandheldItem(ResourceLocation resourceLocation) { - return fromPattern(PatternsHelper.createItemHandheld(resourceLocation)); - } - - public static BlockModel createBlockItem(ResourceLocation resourceLocation) { - Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_BLOCK, resourceLocation); - return fromPattern(pattern); - } - - public static BlockModel createBlockEmpty(ResourceLocation resourceLocation) { - Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_EMPTY, resourceLocation); - return fromPattern(pattern); - } - - public static MultiVariant createMultiVariant(ResourceLocation resourceLocation, Transformation transform, boolean uvLock) { - Variant variant = new Variant(resourceLocation, transform, uvLock, 1); - return new MultiVariant(Lists.newArrayList(variant)); - } - - public static MultiVariant createBlockSimple(ResourceLocation resourceLocation) { - return createMultiVariant(resourceLocation, Transformation.identity(), false); - } - - public static MultiVariant createFacingModel(ResourceLocation resourceLocation, Direction facing, boolean uvLock, boolean inverted) { - if (inverted) { - facing = facing.getOpposite(); - } - BlockModelRotation rotation = BlockModelRotation.by(0, (int) facing.toYRot()); - return createMultiVariant(resourceLocation, rotation.getRotation(), uvLock); - } - - public static MultiVariant createRotatedModel(ResourceLocation resourceLocation, Direction.Axis axis) { - BlockModelRotation rotation = BlockModelRotation.X0_Y0; - switch (axis) { - case X: - rotation = BlockModelRotation.X90_Y90; - break; - case Z: - rotation = BlockModelRotation.X90_Y0; - break; - default: - break; - } - return createMultiVariant(resourceLocation, rotation.getRotation(), false); - } - - public static MultiVariant createRandomTopModel(ResourceLocation resourceLocation) { - return new MultiVariant(Lists.newArrayList( - new Variant(resourceLocation, Transformation.identity(), false, 1), - new Variant(resourceLocation, BlockModelRotation.X0_Y90.getRotation(), false, 1), - new Variant(resourceLocation, BlockModelRotation.X0_Y180.getRotation(), false, 1), - new Variant(resourceLocation, BlockModelRotation.X0_Y270.getRotation(), false, 1) - )); - } - - public static class MultiPartBuilder { - - //private final static MultiPartBuilder BUILDER = new MultiPartBuilder(); - - public static MultiPartBuilder create(StateDefinition stateDefinition) { - // BUILDER.stateDefinition = stateDefinition; - //BUILDER.modelParts.clear(); - // return BUILDER; - return new MultiPartBuilder(stateDefinition); - } - - private final List modelParts = Lists.newArrayList(); - private StateDefinition stateDefinition; - - private MultiPartBuilder(StateDefinition stateDefinition) { - this.stateDefinition = stateDefinition; - } - - public ModelPart part(ResourceLocation modelId) { - ModelPart part = new ModelPart(modelId); - return part; - } - - public MultiPart build() { - if (modelParts.size() > 0) { - List selectors = Lists.newArrayList(); - modelParts.forEach(modelPart -> { - MultiVariant variant = createMultiVariant(modelPart.modelId, modelPart.transform, modelPart.uvLock); - selectors.add(new Selector(modelPart.condition, variant)); - }); - modelParts.clear(); - return new MultiPart(stateDefinition, selectors); - } - throw new IllegalStateException("At least one model part need to be created."); - } - - public class ModelPart { - private final ResourceLocation modelId; - private Transformation transform = Transformation.identity(); - private Condition condition = Condition.TRUE; - private boolean uvLock = false; - - private ModelPart(ResourceLocation modelId) { - this.modelId = modelId; - } - - public ModelPart setCondition(Function condition) { - this.condition = stateDefinition -> condition::apply; - return this; - } - - public ModelPart setTransformation(Transformation transform) { - this.transform = transform; - return this; - } - - public ModelPart setUVLock(boolean value) { - this.uvLock = value; - return this; - } - - public void add() { - modelParts.add(this); - } - } - } -} diff --git a/src/main/java/ru/bclib/client/models/OBJBlockModel.java b/src/main/java/ru/bclib/client/models/OBJBlockModel.java deleted file mode 100644 index 709e6772..00000000 --- a/src/main/java/ru/bclib/client/models/OBJBlockModel.java +++ /dev/null @@ -1,299 +0,0 @@ -package ru.bclib.client.models; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.mojang.datafixers.util.Pair; -import com.mojang.math.Vector3f; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; - -import ru.bclib.BCLib; -import ru.bclib.util.BlocksHelper; -import ru.bclib.util.MHelper; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Random;import net.minecraft.util.RandomSource; -import java.util.Set; -import java.util.function.Function; - -@Environment(EnvType.CLIENT) -public class OBJBlockModel implements UnbakedModel, BakedModel { - private static final Vector3f[] POSITIONS = new Vector3f[] { new Vector3f(), new Vector3f(), new Vector3f() }; - - protected final Map> quadsUnbakedMap = Maps.newEnumMap(Direction.class); - protected final Map> quadsBakedMap = Maps.newEnumMap(Direction.class); - protected final List quadsUnbaked = Lists.newArrayList(); - protected final List quadsBaked = Lists.newArrayList(); - - protected TextureAtlasSprite[] sprites; - protected ItemTransforms transforms; - protected ItemOverrides overrides; - - protected List materials; - protected boolean useCulling; - protected boolean useShading; - protected byte particleIndex; - - public OBJBlockModel(ResourceLocation location, Vector3f offset, boolean useCulling, boolean useShading, byte particleIndex, ResourceLocation... textureIDs) { - for (Direction dir: BlocksHelper.DIRECTIONS) { - quadsUnbakedMap.put(dir, Lists.newArrayList()); - quadsBakedMap.put(dir, Lists.newArrayList()); - } - - transforms = ItemTransforms.NO_TRANSFORMS; - overrides = ItemOverrides.EMPTY; - materials = new ArrayList<>(textureIDs.length); - sprites = new TextureAtlasSprite[textureIDs.length]; - this.particleIndex = particleIndex; - this.useCulling = useCulling; - this.useShading = useShading; - loadModel(location, offset, (byte) (textureIDs.length - 1)); - - for (int i = 0; i < textureIDs.length; i++) { - materials.add(new Material(TextureAtlas.LOCATION_BLOCKS, textureIDs[i])); - } - } - - // UnbakedModel // - - @Override - public Collection getDependencies() { - return Collections.emptyList(); - } - - @Override - public Collection getMaterials(Function function, Set> set) { - return materials; - } - - @Nullable - @Override - public BakedModel bake(ModelBakery modelBakery, Function textureGetter, ModelState modelState, ResourceLocation resourceLocation) { - for (int i = 0; i < sprites.length; i++) { - sprites[i] = textureGetter.apply(materials.get(i)); - } - quadsBaked.clear(); - quadsUnbaked.forEach(quad -> quadsBaked.add(quad.bake(sprites, modelState))); - for (Direction dir: BlocksHelper.DIRECTIONS) { - List unbaked = quadsUnbakedMap.get(dir); - List baked = quadsBakedMap.get(dir); - baked.clear(); - unbaked.forEach(quad -> baked.add(quad.bake(sprites, modelState))); - } - return this; - } - - // Baked Model // - - @Override - public List getQuads(@Nullable BlockState blockState, @Nullable Direction direction, RandomSource random) { - return direction == null ? quadsBaked : quadsBakedMap.get(direction); - } - - @Override - public boolean useAmbientOcclusion() { - return true; - } - - @Override - public boolean isGui3d() { - return true; - } - - @Override - public boolean usesBlockLight() { - return true; - } - - @Override - public boolean isCustomRenderer() { - return false; - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return sprites[particleIndex]; - } - - @Override - public ItemTransforms getTransforms() { - return transforms; - } - - @Override - public ItemOverrides getOverrides() { - return overrides; - } - - private Resource getResource(ResourceLocation location) { - return Minecraft.getInstance().getResourceManager().getResource(location).orElse(null); - } - - private void loadModel(ResourceLocation location, Vector3f offset, byte maxIndex) { - Resource resource = getResource(location); - if (resource == null) { - return; - } - InputStream input = null; - try { - input = resource.open(); - } catch (IOException e) { - BCLib.LOGGER.error("Unable to load Model", e); - throw new RuntimeException(e); - } - - List vertecies = new ArrayList<>(12); - List uvs = new ArrayList<>(8); - - List vertexIndex = new ArrayList<>(4); - List uvIndex = new ArrayList<>(4); - - byte materialIndex = -1; - - try { - InputStreamReader streamReader = new InputStreamReader(input); - BufferedReader reader = new BufferedReader(streamReader); - String string; - - while ((string = reader.readLine()) != null) { - if (string.startsWith("usemtl")) { - materialIndex++; - if (materialIndex > maxIndex) { - materialIndex = maxIndex; - } - } - else if (string.startsWith("vt")) { - String[] uv = string.split(" "); - uvs.add(Float.parseFloat(uv[1])); - uvs.add(Float.parseFloat(uv[2])); - } - else if (string.startsWith("v")) { - String[] vert = string.split(" "); - for (int i = 1; i < 4; i++) { - vertecies.add(Float.parseFloat(vert[i])); - } - } - else if (string.startsWith("f")) { - String[] members = string.split(" "); - if (members.length != 5) { - System.out.println("Only quads in OBJ are supported! Model [" + location + "] has n-gons or triangles!"); - continue; - } - vertexIndex.clear(); - uvIndex.clear(); - - for (int i = 1; i < members.length; i++) { - String member = members[i]; - - if (member.contains("/")) { - String[] sub = member.split("/"); - vertexIndex.add(Integer.parseInt(sub[0]) - 1); // Vertex - uvIndex.add(Integer.parseInt(sub[1]) - 1); // UV - } - else { - vertexIndex.add(Integer.parseInt(member) - 1); // Vertex - } - } - - boolean hasUV = !uvIndex.isEmpty(); - UnbakedQuad quad = new UnbakedQuad(); - for (int i = 0; i < 4; i++) { - int index = vertexIndex.get(i) * 3; - int quadIndex = i * 5; - quad.addData(quadIndex++, vertecies.get(index++) + offset.x()); // X - quad.addData(quadIndex++, vertecies.get(index++) + offset.y()); // Y - quad.addData(quadIndex++, vertecies.get(index) + offset.z()); // Z - if (hasUV) { - index = uvIndex.get(i) * 2; - quad.addData(quadIndex++, uvs.get(index++) * 16F); // U - quad.addData(quadIndex, (1 - uvs.get(index)) * 16F); // V - } - } - quad.setSpriteIndex(materialIndex); - if (useShading) { - Direction dir = getNormalDirection(quad); - quad.setDirection(dir); - quad.setShading(true); - } - if (useCulling) { - Direction dir = getCullingDirection(quad); - if (dir == null) { - quadsUnbaked.add(quad); - } - else { - quadsUnbakedMap.get(dir).add(quad); - } - } - else { - quadsUnbaked.add(quad); - } - } - } - - reader.close(); - streamReader.close(); - input.close(); - } - catch (IOException e) { - e.printStackTrace(); - } - - if (materialIndex < 0) { - quadsUnbaked.forEach(quad -> quad.setSpriteIndex(0)); - quadsUnbakedMap.values().forEach(list -> list.forEach(quad -> quad.setSpriteIndex(0))); - } - } - - private Direction getNormalDirection(UnbakedQuad quad) { - Vector3f pos = quad.getPos(0, POSITIONS[0]); - Vector3f dirA = quad.getPos(1, POSITIONS[1]); - Vector3f dirB = quad.getPos(2, POSITIONS[2]); - dirA.sub(pos); - dirB.sub(pos); - pos = MHelper.cross(dirA, dirB); - return Direction.getNearest(pos.x(), pos.y(), pos.z()); - } - - @Nullable - private Direction getCullingDirection(UnbakedQuad quad) { - Direction dir = null; - for (int i = 0; i < 4; i++) { - Vector3f pos = quad.getPos(i, POSITIONS[0]); - if (pos.x() < 1 && pos.x() > 0 && pos.y() < 1 && pos.y() > 0 && pos.z() < 1 && pos.z() > 0) { - return null; - } - Direction newDir = Direction.getNearest(pos.x() - 0.5F, pos.y() - 0.5F, pos.z() - 0.5F); - if (dir == null) { - dir = newDir; - } - else if (newDir != dir) { - return null; - } - } - return dir; - } -} diff --git a/src/main/java/ru/bclib/client/models/OBJModelBuilder.java b/src/main/java/ru/bclib/client/models/OBJModelBuilder.java deleted file mode 100644 index 7316985b..00000000 --- a/src/main/java/ru/bclib/client/models/OBJModelBuilder.java +++ /dev/null @@ -1,103 +0,0 @@ -package ru.bclib.client.models; - -import com.google.common.collect.Lists; -import com.mojang.math.Vector3f; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.resources.ResourceLocation; - -import java.util.List; - -@Environment(EnvType.CLIENT) -public class OBJModelBuilder { - private static final OBJModelBuilder INSTANCE = new OBJModelBuilder(); - private final List textures = Lists.newArrayList(); - private Vector3f offset = new Vector3f(); - private ResourceLocation modelLocation; - private ResourceLocation particles; - private boolean useCulling; - private boolean useShading; - - private OBJModelBuilder() {} - - /** - * Start a new bodel building process, clears data of previous builder. - * @return {@link OBJModelBuilder} instance. - */ - public static OBJModelBuilder start(ResourceLocation modelLocation) { - INSTANCE.modelLocation = modelLocation; - INSTANCE.offset.set(0, 0, 0); - INSTANCE.useCulling = true; - INSTANCE.useShading = true; - INSTANCE.particles = null; - INSTANCE.textures.clear(); - return INSTANCE; - } - - /** - * Add texture to the model. All textures have indexes with same order as in source OBJ model. - * @param texture {@link ResourceLocation} texture ID. - * @return this {@link OBJModelBuilder}. - */ - public OBJModelBuilder addTexture(ResourceLocation texture) { - textures.add(texture); - return this; - } - - /** - * Culling used to remove block faces if they are on block faces or outside of the block to reduce faces count in rendering. - * Opaque blocks shoud have this as true to reduce geometry issues, block like plants should have this as false. - * Default value is {@code true}. - * @param useCulling {@link Boolean}. - * @return this {@link OBJModelBuilder}. - */ - public OBJModelBuilder useCulling(boolean useCulling) { - this.useCulling = useCulling; - return this; - } - - /** - * Shading tints block faces in shades of gray to immitate volume in MC rendering. - * Blocks like plants don't have shading, most full opaque blocks - have. - * Default value is {@code true}. - * @param useShading {@link Boolean}. - * @return this {@link OBJModelBuilder}. - */ - public OBJModelBuilder useShading(boolean useShading) { - this.useShading = useShading; - return this; - } - - /** - * Set particle texture for this model. - * Not required, if texture is not selected the first texture will be used instead of it. - * @param texture {@link ResourceLocation} texture ID. - * @return this {@link OBJModelBuilder}. - */ - public OBJModelBuilder setParticlesTexture(ResourceLocation texture) { - this.particles = texture; - return this; - } - - public OBJModelBuilder setOffset(float x, float y, float z) { - this.offset.set(x, y, z); - return this; - } - - /** - * Builds model from all required data. - * @return {@link OBJBlockModel}. - */ - public OBJBlockModel build() { - byte particleIndex = 0; - if (particles != null) { - particleIndex = (byte) textures.indexOf(particles); - if (particleIndex < 0) { - particleIndex = (byte) textures.size(); - textures.add(particles); - } - } - ResourceLocation[] sprites = textures.toArray(new ResourceLocation[textures.size()]); - return new OBJBlockModel(modelLocation, offset, useCulling, useShading, particleIndex, sprites); - } -} diff --git a/src/main/java/ru/bclib/client/models/PatternsHelper.java b/src/main/java/ru/bclib/client/models/PatternsHelper.java deleted file mode 100644 index 375f2d66..00000000 --- a/src/main/java/ru/bclib/client/models/PatternsHelper.java +++ /dev/null @@ -1,75 +0,0 @@ -package ru.bclib.client.models; - -import com.google.common.collect.Maps; -import net.minecraft.client.Minecraft; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -public class PatternsHelper { - private static final Map JSON_CACHE = Maps.newConcurrentMap(); - - public static Optional createItemGenerated(ResourceLocation itemId) { - return createJson(BasePatterns.ITEM_GENERATED, itemId); - } - - public static Optional createItemHandheld(ResourceLocation itemId) { - return createJson(BasePatterns.ITEM_HANDHELD, itemId); - } - - public static Optional createBlockSimple(ResourceLocation blockId) { - return createJson(BasePatterns.BLOCK_BASE, blockId); - } - - public static Optional createBlockEmpty(ResourceLocation blockId) { - return createJson(BasePatterns.BLOCK_EMPTY, blockId); - } - - public static Optional createBlockPillar(ResourceLocation blockId) { - return createJson(BasePatterns.BLOCK_PILLAR, blockId); - } - - public static Optional createBlockBottomTop(ResourceLocation blockId) { - return createJson(BasePatterns.BLOCK_BOTTOM_TOP, blockId); - } - - public static Optional createBlockColored(ResourceLocation blockId) { - return createJson(BasePatterns.BLOCK_COLORED, blockId); - } - - public static Optional createJson(ResourceLocation patternId, ResourceLocation blockId) { - Map textures = Maps.newHashMap(); - textures.put("%modid%", blockId.getNamespace()); - textures.put("%texture%", blockId.getPath()); - return createJson(patternId, textures); - } - - public static Optional createJson(ResourceLocation patternId, Map textures) { - ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); - Optional patternRes = resourceManager.getResource(patternId); - if (patternRes.isEmpty()) return Optional.empty(); - - try (InputStream input = patternRes.get().open()) { - String json = JSON_CACHE.get(patternId); - if (json == null) { - json = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines().collect(Collectors.joining()); - JSON_CACHE.put(patternId, json); - } - for (Map.Entry texture : textures.entrySet()) { - json = json.replace(texture.getKey(), texture.getValue()); - } - return Optional.of(json); - } - catch (Exception ex) { - return Optional.empty(); - } - } -} diff --git a/src/main/java/ru/bclib/client/models/UnbakedQuad.java b/src/main/java/ru/bclib/client/models/UnbakedQuad.java deleted file mode 100644 index dbf5f6d8..00000000 --- a/src/main/java/ru/bclib/client/models/UnbakedQuad.java +++ /dev/null @@ -1,68 +0,0 @@ -package ru.bclib.client.models; - -import com.mojang.math.Matrix4f; -import com.mojang.math.Vector3f; -import com.mojang.math.Vector4f; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.Direction; - -@Environment(EnvType.CLIENT) -public class UnbakedQuad { - private static final Vector4f POS = new Vector4f(); - private float[] data = new float[20]; // 4 points with 3 positions and 2 uvs, 4 * (3 + 2) - private Direction dir = Direction.UP; - private boolean useShading = false; - private int spriteIndex; - - public void addData(int index, float value) { - data[index] = value; - } - - public void setSpriteIndex(int index) { - spriteIndex = index; - } - - public void setDirection(Direction dir) { - this.dir = dir; - } - - public void setShading(boolean useShading) { - this.useShading = useShading; - } - - public Vector3f getPos(int index, Vector3f result) { - int dataIndex = index * 5; - float x = data[dataIndex++]; - float y = data[dataIndex++]; - float z = data[dataIndex]; - result.set(x, y, z); - return result; - } - - public BakedQuad bake(TextureAtlasSprite[] sprites, ModelState modelState) { - Matrix4f matrix = modelState.getRotation().getMatrix(); - TextureAtlasSprite sprite = sprites[spriteIndex]; - int[] vertexData = new int[32]; - for (int i = 0; i < 4; i++) { - int index = i << 3; - int dataIndex = i * 5; - float x = data[dataIndex++]; // X - float y = data[dataIndex++]; // Y - float z = data[dataIndex++]; // Z - POS.set(x, y, z, 0); - POS.transform(matrix); - vertexData[index] = Float.floatToIntBits(POS.x()); // X - vertexData[index | 1] = Float.floatToIntBits(POS.y()); // Y - vertexData[index | 2] = Float.floatToIntBits(POS.z()); // Z - vertexData[index | 3] = -1; // Unknown constant - vertexData[index | 4] = Float.floatToIntBits(sprite.getU(data[dataIndex++])); // U - vertexData[index | 5] = Float.floatToIntBits(sprite.getV(data[dataIndex])); // V - } - // vertices, tint index, direction, sprite, shade - return new BakedQuad(vertexData, 0, dir, sprites[spriteIndex], useShading); - } -} diff --git a/src/main/java/ru/bclib/client/render/BCLRenderLayer.java b/src/main/java/ru/bclib/client/render/BCLRenderLayer.java deleted file mode 100644 index 796edfa5..00000000 --- a/src/main/java/ru/bclib/client/render/BCLRenderLayer.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.bclib.client.render; - -public enum BCLRenderLayer { - CUTOUT, TRANSLUCENT; -} diff --git a/src/main/java/ru/bclib/client/render/BaseChestBlockEntityRenderer.java b/src/main/java/ru/bclib/client/render/BaseChestBlockEntityRenderer.java deleted file mode 100644 index 42e2f689..00000000 --- a/src/main/java/ru/bclib/client/render/BaseChestBlockEntityRenderer.java +++ /dev/null @@ -1,175 +0,0 @@ -package ru.bclib.client.render; - -import com.google.common.collect.Maps; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.math.Vector3f; -import it.unimi.dsi.fastutil.floats.Float2FloatFunction; -import it.unimi.dsi.fastutil.ints.Int2IntFunction; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.model.geom.ModelPart; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; -import net.minecraft.client.renderer.blockentity.BrightnessCombiner; -import net.minecraft.core.Direction; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.AbstractChestBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.DoubleBlockCombiner; -import net.minecraft.world.level.block.DoubleBlockCombiner.NeighborCombineResult; -import net.minecraft.world.level.block.entity.ChestBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.ChestType; -import ru.bclib.blockentities.BaseChestBlockEntity; -import ru.bclib.client.models.BaseChestBlockModel; - -import java.util.HashMap; - -@Environment(EnvType.CLIENT) -public class BaseChestBlockEntityRenderer implements BlockEntityRenderer { - private static final HashMap LAYERS = Maps.newHashMap(); - private static final RenderType[] RENDER_TYPES; - - private static final int ID_NORMAL = 0; - private static final int ID_LEFT = 1; - private static final int ID_RIGHT = 2; - - private final BaseChestBlockModel chestModel; - - public BaseChestBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { - super(); - chestModel = new BaseChestBlockModel(BaseChestBlockModel.getTexturedModelData().bakeRoot()); - } - - public void render(BaseChestBlockEntity entity, float tickDelta, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay) { - Level world = entity.getLevel(); - boolean worldExists = world != null; - BlockState blockState = worldExists ? entity.getBlockState() : Blocks.CHEST.defaultBlockState() - .setValue( - ChestBlock.FACING, - Direction.SOUTH - ); - ChestType chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE; - Block block = blockState.getBlock(); - if (block instanceof AbstractChestBlock) { - AbstractChestBlock abstractChestBlock = (AbstractChestBlock) block; - boolean isDouble = chestType != ChestType.SINGLE; - float f = ((Direction) blockState.getValue(ChestBlock.FACING)).toYRot(); - NeighborCombineResult propertySource; - - matrices.pushPose(); - matrices.translate(0.5D, 0.5D, 0.5D); - matrices.mulPose(Vector3f.YP.rotationDegrees(-f)); - matrices.translate(-0.5D, -0.5D, -0.5D); - - if (worldExists) { - propertySource = abstractChestBlock.combine(blockState, world, entity.getBlockPos(), true); - } - else { - propertySource = DoubleBlockCombiner.Combiner::acceptNone; - } - - float pitch = ((Float2FloatFunction) propertySource.apply(ChestBlock.opennessCombiner(entity))).get( - tickDelta); - pitch = 1.0F - pitch; - pitch = 1.0F - pitch * pitch * pitch; - @SuppressWarnings({ - "unchecked", - "rawtypes" - }) int blockLight = ((Int2IntFunction) propertySource.apply(new BrightnessCombiner())).applyAsInt(light); - - VertexConsumer vertexConsumer = getConsumer(vertexConsumers, block, chestType); - - if (isDouble) { - if (chestType == ChestType.LEFT) { - renderParts( - matrices, - vertexConsumer, - chestModel.partLeftA, - chestModel.partLeftB, - chestModel.partLeftC, - pitch, - blockLight, - overlay - ); - } - else { - renderParts( - matrices, - vertexConsumer, - chestModel.partRightA, - chestModel.partRightB, - chestModel.partRightC, - pitch, - blockLight, - overlay - ); - } - } - else { - renderParts( - matrices, - vertexConsumer, - chestModel.partA, - chestModel.partB, - chestModel.partC, - pitch, - blockLight, - overlay - ); - } - - matrices.popPose(); - } - } - - private void renderParts(PoseStack matrices, VertexConsumer vertices, ModelPart modelPart, ModelPart modelPart2, ModelPart modelPart3, float pitch, int light, int overlay) { - modelPart.xRot = -(pitch * 1.5707964F); - modelPart2.xRot = modelPart.xRot; - modelPart.render(matrices, vertices, light, overlay); - modelPart2.render(matrices, vertices, light, overlay); - modelPart3.render(matrices, vertices, light, overlay); - } - - private static RenderType getChestTexture(ChestType type, RenderType[] layers) { - return switch (type) { - case LEFT -> layers[ID_LEFT]; - case RIGHT -> layers[ID_RIGHT]; - default -> layers[ID_NORMAL]; - }; - } - - public static VertexConsumer getConsumer(MultiBufferSource provider, Block block, ChestType chestType) { - RenderType[] layers = LAYERS.getOrDefault(block, RENDER_TYPES); - return provider.getBuffer(getChestTexture(chestType, layers)); - } - - public static void registerRenderLayer(Block block) { - ResourceLocation blockId = Registry.BLOCK.getKey(block); - String modId = blockId.getNamespace(); - String path = blockId.getPath(); - LAYERS.put( - block, - new RenderType[] { - RenderType.entityCutout(new ResourceLocation(modId, "textures/entity/chest/" + path + ".png")), - RenderType.entityCutout(new ResourceLocation(modId, "textures/entity/chest/" + path + "_left.png")), - RenderType.entityCutout(new ResourceLocation(modId, "textures/entity/chest/" + path + "_right.png")) - } - ); - } - - static { - RENDER_TYPES = new RenderType[] { - RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal.png")), - RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal_left.png")), - RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal_right.png")) - }; - } -} diff --git a/src/main/java/ru/bclib/client/render/BaseSignBlockEntityRenderer.java b/src/main/java/ru/bclib/client/render/BaseSignBlockEntityRenderer.java deleted file mode 100644 index 6d408a46..00000000 --- a/src/main/java/ru/bclib/client/render/BaseSignBlockEntityRenderer.java +++ /dev/null @@ -1,189 +0,0 @@ -package ru.bclib.client.render; - -import com.google.common.collect.Maps; -import com.mojang.blaze3d.platform.NativeImage; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.math.Vector3f; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.model.geom.ModelLayers; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.Sheets; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; -import net.minecraft.client.renderer.blockentity.SignRenderer; -import net.minecraft.client.resources.model.Material; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.FormattedCharSequence; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SignBlock; -import net.minecraft.world.level.block.StandingSignBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.WoodType; -import net.minecraft.world.phys.Vec3; -import ru.bclib.blockentities.BaseSignBlockEntity; -import ru.bclib.blocks.BaseSignBlock; - -import java.util.HashMap; -import java.util.List; - -public class BaseSignBlockEntityRenderer implements BlockEntityRenderer { - private static final HashMap RENDER_TYPES = Maps.newHashMap(); - private static final int OUTLINE_RENDER_DISTANCE = Mth.square(16); - private static final RenderType RENDER_TYPE; - private final SignRenderer.SignModel model; - private final Font font; - - - public BaseSignBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { - super(); - this.font = ctx.getFont(); - model = new SignRenderer.SignModel(ctx.bakeLayer(ModelLayers.createSignModelName(WoodType.OAK))); - } - - public void render(BaseSignBlockEntity signBlockEntity, float tickDelta, PoseStack matrixStack, MultiBufferSource provider, int light, int overlay) { - BlockState state = signBlockEntity.getBlockState(); - - matrixStack.pushPose(); - - - matrixStack.translate(0.5D, 0.5D, 0.5D); - float angle = -((float) (state.getValue(StandingSignBlock.ROTATION) * 360) / 16.0F); - - BlockState blockState = signBlockEntity.getBlockState(); - if (blockState.getValue(BaseSignBlock.FLOOR)) { - matrixStack.mulPose(Vector3f.YP.rotationDegrees(angle)); - model.stick.visible = true; - } - else { - matrixStack.mulPose(Vector3f.YP.rotationDegrees(angle + 180)); - matrixStack.translate(0.0D, -0.3125D, -0.4375D); - model.stick.visible = false; - } - - matrixStack.pushPose(); - matrixStack.scale(0.6666667F, -0.6666667F, -0.6666667F); - VertexConsumer vertexConsumer = getConsumer(provider, state.getBlock()); - - model.root.render(matrixStack, vertexConsumer, light, overlay); - matrixStack.popPose(); - matrixStack.translate(0.0D, 0.3333333432674408D, 0.046666666865348816D); - matrixStack.scale(0.010416667F, -0.010416667F, 0.010416667F); - int m = signBlockEntity.getColor().getTextColor(); - int n = (int) (NativeImage.getR(m) * 0.4D); - int o = (int) (NativeImage.getG(m) * 0.4D); - int p = (int) (NativeImage.getB(m) * 0.4D); - int q = NativeImage.combine(0, p, o, n); - - FormattedCharSequence[] formattedCharSequences = signBlockEntity.getRenderMessages( - Minecraft.getInstance() - .isTextFilteringEnabled(), - (component) -> { - List list = this.font.split(component, 90); - return list.isEmpty() ? FormattedCharSequence.EMPTY : (FormattedCharSequence) list.get(0); - } - ); - int drawColor; - boolean drawOutlined; - int drawLight; - if (signBlockEntity.hasGlowingText()) { - drawColor = signBlockEntity.getColor().getTextColor(); - drawOutlined = isOutlineVisible(signBlockEntity, drawColor); - drawLight = 15728880; - } - else { - drawColor = m; - drawOutlined = false; - drawLight = light; - } - - for (int s = 0; s < 4; ++s) { - FormattedCharSequence formattedCharSequence = formattedCharSequences[s]; - float t = (float) (-this.font.width(formattedCharSequence) / 2); - if (drawOutlined) { - this.font.drawInBatch8xOutline( - formattedCharSequence, - t, - (float) (s * 10 - 20), - drawColor, - m, - matrixStack.last().pose(), - provider, - drawLight - ); - } - else { - this.font.drawInBatch( - (FormattedCharSequence) formattedCharSequence, - t, - (float) (s * 10 - 20), - drawColor, - false, - matrixStack.last().pose(), - provider, - false, - 0, - drawLight - ); - } - } - - - matrixStack.popPose(); - } - - - private static boolean isOutlineVisible(BaseSignBlockEntity signBlockEntity, int i) { - if (i == DyeColor.BLACK.getTextColor()) { - return true; - } - else { - Minecraft minecraft = Minecraft.getInstance(); - LocalPlayer localPlayer = minecraft.player; - if (localPlayer != null && minecraft.options.getCameraType().isFirstPerson() && localPlayer.isScoping()) { - return true; - } - else { - Entity entity = minecraft.getCameraEntity(); - return entity != null && entity.distanceToSqr(Vec3.atCenterOf(signBlockEntity.getBlockPos())) < (double) OUTLINE_RENDER_DISTANCE; - } - } - } - - public static WoodType getSignType(Block block) { - WoodType signType2; - if (block instanceof SignBlock) { - signType2 = ((SignBlock) block).type(); - } - else { - signType2 = WoodType.OAK; - } - - return signType2; - } - - public static Material getModelTexture(Block block) { - return Sheets.getSignMaterial(getSignType(block)); - } - - public static VertexConsumer getConsumer(MultiBufferSource provider, Block block) { - return provider.getBuffer(RENDER_TYPES.getOrDefault(block, RENDER_TYPE)); - } - - public static void registerRenderLayer(Block block) { - ResourceLocation blockId = Registry.BLOCK.getKey(block); - RenderType layer = RenderType.entitySolid(new ResourceLocation(blockId.getNamespace(), "textures/entity/sign/" + blockId.getPath() + ".png")); - RENDER_TYPES.put(block, layer); - } - - static { - RENDER_TYPE = RenderType.entitySolid(new ResourceLocation("textures/entity/signs/oak.png")); - } -} diff --git a/src/main/java/ru/bclib/client/render/CustomFogRenderer.java b/src/main/java/ru/bclib/client/render/CustomFogRenderer.java deleted file mode 100644 index 76a72637..00000000 --- a/src/main/java/ru/bclib/client/render/CustomFogRenderer.java +++ /dev/null @@ -1,154 +0,0 @@ -package ru.bclib.client.render; - -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.Camera; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.util.Mth; -import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.material.FogType; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.config.Configs; -import ru.bclib.util.BackgroundInfo; -import ru.bclib.util.MHelper; -import ru.bclib.world.biomes.BCLBiome; - -public class CustomFogRenderer { - private static final MutableBlockPos LAST_POS = new MutableBlockPos(0, -100, 0); - 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, float viewDistance, boolean thickFog) { - if (!Configs.CLIENT_CONFIG.renderCustomFog()) { - return false; - } - - FogType fogType = camera.getFluidInCamera(); - if (fogType != FogType.NONE) { - 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(thickFog, entity.level)) { - fogStart = viewDistance * 0.05F / fog; - fogEnd = Math.min(viewDistance, 192.0F) * 0.5F / fog; - } - else { - fogStart = viewDistance * 0.25F / fog; // In vanilla - 0 - 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 { - float delta = (float) duration / 20F; - BackgroundInfo.blindness = delta; - fogStart = Mth.lerp(delta, fogStart, 0); - fogEnd = Mth.lerp(delta, fogEnd, fogEnd * 0.03F); - } - } - else { - BackgroundInfo.blindness = 0; - } - } - - RenderSystem.setShaderFogStart(fogStart); - RenderSystem.setShaderFogEnd(fogEnd); - - return true; - } - - private static boolean thickFog(boolean thickFog, Level level) { - if (!thickFog) { - return false; - } - if (level.dimension() == Level.NETHER) { - return Configs.CLIENT_CONFIG.netherThickFog(); - } - 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) { - Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value(); - return BiomeAPI.getRenderBiome(biome) == BiomeAPI.EMPTY_BIOME; - } - - private static float getFogDensityI(Level level, int x, int y, int z) { - Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value(); - BCLBiome renderBiome = BiomeAPI.getRenderBiome(biome); - return renderBiome.getFogDensity(); - } - - private static float getFogDensity(Level level, double x, double y, double z) { - int x1 = MHelper.floor(x / GRID_SIZE) * GRID_SIZE; - int y1 = MHelper.floor(y / GRID_SIZE) * GRID_SIZE; - int z1 = MHelper.floor(z / GRID_SIZE) * GRID_SIZE; - float dx = (float) (x - x1) / GRID_SIZE; - float dy = (float) (y - y1) / GRID_SIZE; - float dz = (float) (z - z1) / GRID_SIZE; - - if (LAST_POS.getX() != x1 || LAST_POS.getY() != y1 || LAST_POS.getZ() != z1) { - int x2 = x1 + GRID_SIZE; - int y2 = y1 + GRID_SIZE; - int z2 = z1 + GRID_SIZE; - LAST_POS.set(x1, y1, z1); - FOG_DENSITY[0] = getFogDensityI(level, x1, y1, z1); - FOG_DENSITY[1] = getFogDensityI(level, x2, y1, z1); - FOG_DENSITY[2] = getFogDensityI(level, x1, y2, z1); - FOG_DENSITY[3] = getFogDensityI(level, x2, y2, z1); - FOG_DENSITY[4] = getFogDensityI(level, x1, y1, z2); - FOG_DENSITY[5] = getFogDensityI(level, x2, y1, z2); - FOG_DENSITY[6] = getFogDensityI(level, x1, y2, z2); - FOG_DENSITY[7] = getFogDensityI(level, x2, y2, z2); - } - - float a = Mth.lerp(dx, FOG_DENSITY[0], FOG_DENSITY[1]); - float b = Mth.lerp(dx, FOG_DENSITY[2], FOG_DENSITY[3]); - float c = Mth.lerp(dx, FOG_DENSITY[4], FOG_DENSITY[5]); - float d = Mth.lerp(dx, FOG_DENSITY[6], FOG_DENSITY[7]); - - a = Mth.lerp(dy, a, b); - b = Mth.lerp(dy, c, d); - - return Mth.lerp(dz, a, b); - } -} diff --git a/src/main/java/ru/bclib/client/render/EmissiveTextureInfo.java b/src/main/java/ru/bclib/client/render/EmissiveTextureInfo.java deleted file mode 100644 index 0623a9df..00000000 --- a/src/main/java/ru/bclib/client/render/EmissiveTextureInfo.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.bclib.client.render; - -import com.google.common.collect.Sets; -import net.minecraft.resources.ResourceLocation; - -import java.util.Set; - -public class EmissiveTextureInfo { - private static final Set EMISSIVE_TEXTURES = Sets.newHashSet(); - private static final Set EMISSIVE_BLOCKS = Sets.newHashSet(); - - public static void clear() { - EMISSIVE_TEXTURES.clear(); - EMISSIVE_BLOCKS.clear(); - } - - public static void addTexture(ResourceLocation texture) { - EMISSIVE_TEXTURES.add(texture); - } - - public static void addBlock(ResourceLocation blockID) { - EMISSIVE_BLOCKS.add(blockID); - } - - public static boolean isEmissiveTexture(ResourceLocation texture) { - return EMISSIVE_TEXTURES.contains(texture); - } - - public static boolean isEmissiveBlock(ResourceLocation blockID) { - return EMISSIVE_BLOCKS.contains(blockID); - } -} diff --git a/src/main/java/ru/bclib/client/sound/BlockSounds.java b/src/main/java/ru/bclib/client/sound/BlockSounds.java deleted file mode 100644 index d7fc73a6..00000000 --- a/src/main/java/ru/bclib/client/sound/BlockSounds.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.bclib.client.sound; - -import net.minecraft.sounds.SoundEvents; -import net.minecraft.world.level.block.SoundType; - -public class BlockSounds { - public static final SoundType TERRAIN_SOUND = new SoundType( - 1.0F, - 1.0F, - SoundEvents.STONE_BREAK, - SoundEvents.WART_BLOCK_STEP, - SoundEvents.STONE_PLACE, - SoundEvents.STONE_HIT, - SoundEvents.STONE_FALL - ); -} diff --git a/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java b/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java deleted file mode 100644 index 2fc08408..00000000 --- a/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java +++ /dev/null @@ -1,342 +0,0 @@ -package ru.bclib.complexmaterials; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import org.jetbrains.annotations.Nullable; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.complexmaterials.entry.BlockEntry; -import ru.bclib.complexmaterials.entry.ItemEntry; -import ru.bclib.complexmaterials.entry.RecipeEntry; -import ru.bclib.config.PathConfig; -import ru.bclib.registry.BlockRegistry; -import ru.bclib.registry.ItemRegistry; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public abstract class ComplexMaterial { - private static final Map> RECIPE_ENTRIES = Maps.newHashMap(); - private static final Map> BLOCK_ENTRIES = Maps.newHashMap(); - private static final Map> ITEM_ENTRIES = Maps.newHashMap(); - private static final List MATERIALS = Lists.newArrayList(); - - private final List defaultRecipeEntries = Lists.newArrayList(); - private final List defaultBlockEntries = Lists.newArrayList(); - private final List defaultItemEntries = Lists.newArrayList(); - - private final Map> blockTags = Maps.newHashMap(); - private final Map> itemTags = Maps.newHashMap(); - private final Map blocks = Maps.newHashMap(); - private final Map items = Maps.newHashMap(); - - protected final String baseName; - protected final String modID; - protected final String receipGroupPrefix; - - public ComplexMaterial(String modID, String baseName, String receipGroupPrefix) { - this.baseName = baseName; - this.modID = modID; - this.receipGroupPrefix = receipGroupPrefix; - MATERIALS.add(this); - } - - /** - * Initialize and registers all content inside material, return material itself. - * @param blocksRegistry {@link BlockRegistry} instance to add blocks in; - * @param itemsRegistry {@link ItemRegistry} instance to add items in; - * @param recipeConfig {@link PathConfig} for recipes check. - * @return {@link ComplexMaterial}. - */ - public ComplexMaterial init(BlockRegistry blocksRegistry, ItemRegistry itemsRegistry, PathConfig recipeConfig) { - initTags(); - - final FabricBlockSettings blockSettings = getBlockSettings(); - final FabricItemSettings itemSettings = getItemSettings(itemsRegistry); - initDefault(blockSettings, itemSettings); - - getBlockEntries().forEach(entry -> { - Block block = entry.init(this, blockSettings, blocksRegistry); - blocks.put(entry.getSuffix(), block); - }); - - getItemEntries().forEach(entry -> { - Item item = entry.init(this, itemSettings, itemsRegistry); - items.put(entry.getSuffix(), item); - }); - - initDefaultRecipes(); - getRecipeEntries().forEach(entry -> { - entry.init(this, recipeConfig); - }); - - initFlammable(FlammableBlockRegistry.getDefaultInstance()); - return this; - } - - /** - * Init default content for {@link ComplexMaterial} - blocks and items. - * @param blockSettings {@link FabricBlockSettings} default block settings for this material; - * @param itemSettings {@link FabricItemSettings} default item settings for this material. - */ - protected abstract void initDefault(FabricBlockSettings blockSettings, FabricItemSettings itemSettings); - - /** - * Init custom tags for this {@link ComplexMaterial}, not required. - */ - protected void initTags() {} - - /** - * Init default recipes for this {@link ComplexMaterial}, not required. - */ - protected void initDefaultRecipes() {} - - /** - * Allows to add blocks into Fabric {@link FlammableBlockRegistry} for this {@link ComplexMaterial}, not required. - */ - protected void initFlammable(FlammableBlockRegistry registry) {} - - /** - * Adds custom block tag for this {@link ComplexMaterial}, tag can be created with {@link TagAPI} or you can use one of already created tags. - * @param tag {@link TagKey} for {@link Block} - */ - protected void addBlockTag(TagKey tag) { - String key = tag.location().getPath().replace(getBaseName() + "_", ""); - blockTags.put(key, tag); - } - - /** - * Adds custom item tag for this {@link ComplexMaterial}, tag can be created with {@link TagAPI} or you can use one of already created tags. - * @param tag {@link TagKey} for {@link Item} - */ - protected void addItemTag(TagKey tag) { - String key = tag.location().getPath().replace(getBaseName() + "_", ""); - itemTags.put(key, tag); - } - - /** - * Get custom {@link Block} {@link TagKey} from this {@link ComplexMaterial}. - * @param key {@link String} tag name (path of its {@link ResourceLocation}), for inner tags created inside material its tag suffix. - * @return {@link TagKey} for {@link Block} or {@code null} if nothing is stored. - */ - @Nullable - public TagKey getBlockTag(String key) { - return blockTags.get(key); - } - - /** - * Get custom {@link Item} {@link TagKey} from this {@link ComplexMaterial}. - * @param key {@link String} tag name (path of its {@link ResourceLocation}), for inner tags created inside material its tag suffix. - * @return {@link TagKey} for {@link Item} or {@code null} if nothing is stored. - */ - @Nullable - public TagKey getItemTag(String key) { - return itemTags.get(key); - } - - /** - * Get initiated {@link Block} from this {@link ComplexMaterial}. - * @param key {@link String} block name suffix (example: "mod:custom_log" will have a "log" suffix if "custom" is a base name of this material) - * @return {@link Block} or {@code null} if nothing is stored. - */ - @Nullable - public Block getBlock(String key) { - return blocks.get(key); - } - - /** - * Get initiated {@link Item} from this {@link ComplexMaterial}. - * @param key {@link String} block name suffix (example: "mod:custom_apple" will have a "apple" suffix if "custom" is a base name of this material) - * @return {@link Item} or {@code null} if nothing is stored. - */ - @Nullable - public Item getItem(String key) { - return items.get(key); - } - - /** - * Get default block settings for this material. - * @return {@link FabricBlockSettings} - */ - protected abstract FabricBlockSettings getBlockSettings(); - - /** - * Get default item settings for this material. - * @return {@link FabricItemSettings} - */ - protected FabricItemSettings getItemSettings(ItemRegistry registry) { - return registry.makeItemSettings(); - } - - private Collection getBlockEntries() { - List result = Lists.newArrayList(defaultBlockEntries); - List entries = BLOCK_ENTRIES.get(this.getMaterialID()); - if (entries != null) { - result.addAll(entries); - } - return result; - } - - private Collection getItemEntries() { - List result = Lists.newArrayList(defaultItemEntries); - List entries = ITEM_ENTRIES.get(this.getMaterialID()); - if (entries != null) { - result.addAll(entries); - } - return result; - } - - private Collection getRecipeEntries() { - List result = Lists.newArrayList(defaultRecipeEntries); - List entries = RECIPE_ENTRIES.get(this.getMaterialID()); - if (entries != null) { - result.addAll(entries); - } - return result; - } - - /** - * Get base name of this {@link ComplexMaterial}. - * @return {@link String} name - */ - public String getBaseName() { - return baseName; - } - - /** - * Get mod ID for this {@link ComplexMaterial}. - * @return {@link String} mod ID. - */ - public String getModID() { - return modID; - } - - /** - * Get a unique {@link ResourceLocation} for each material class. - * For example WoodenComplexMaterial will have a "bclib:Wooden_Complex_Material" {@link ResourceLocation}. - * This is used to add custom entries before mods init using Fabric "preLaunch" entry point. - * @return {@link ResourceLocation} for this material - * @see Fabric Documentation: Entrypoint - */ - public abstract ResourceLocation getMaterialID(); - - /** - * Get all initiated block from this {@link ComplexMaterial}. - * @return {@link Collection} of {@link Block}. - */ - public Collection getBlocks() { - return blocks.values(); - } - - /** - * Get all initiated items from this {@link ComplexMaterial}. - * @return {@link Collection} of {@link Item}. - */ - public Collection getItems() { - return items.values(); - } - - /** - * Adds a default {@link BlockEntry} to this {@link ComplexMaterial}. Used to initiate blocks later. - * @param entry {@link BlockEntry} - */ - protected void addBlockEntry(BlockEntry entry) { - defaultBlockEntries.add(entry); - } - - /** - * Replaces or Adds a default {@link BlockEntry} to this {@link ComplexMaterial}. Used to initiate blocks later. - *

- * If this {@link ComplexMaterial} does already contain an entry for the {@link ResourceLocation}, the entry will - * be removed first. - * @param entry {@link BlockEntry} - */ - protected void replaceOrAddBlockEntry(BlockEntry entry) { - int pos = defaultBlockEntries.indexOf(entry); - if (pos>=0) defaultBlockEntries.remove(entry); - - addBlockEntry(entry); - } - - /** - * Adds a default {@link ItemEntry} to this {@link ComplexMaterial}. Used to initiate items later. - * @param entry {@link ItemEntry} - */ - protected void addItemEntry(ItemEntry entry) { - defaultItemEntries.add(entry); - } - - /** - * Adds a default {@link RecipeEntry} to this {@link ComplexMaterial}. Used to initiate items later. - * @param entry {@link RecipeEntry} - */ - protected void addRecipeEntry(RecipeEntry entry) { - defaultRecipeEntries.add(entry); - } - - /** - * Adds a custom {@link BlockEntry} for specified {@link ComplexMaterial} using its {@link ResourceLocation}. - * Used to add custom entry for all instances of {@link ComplexMaterial}. - * Should be called only using Fabric "preLaunch" entry point. - * @param materialName {@link ResourceLocation} id of {@link ComplexMaterial}; - * @param entry {@link BlockEntry}. - * @see Fabric Documentation: Entrypoint - */ - public static void addBlockEntry(ResourceLocation materialName, BlockEntry entry) { - List entries = BLOCK_ENTRIES.get(materialName); - if (entries == null) { - entries = Lists.newArrayList(); - BLOCK_ENTRIES.put(materialName, entries); - } - entries.add(entry); - } - - /** - * Adds a custom {@link ItemEntry} for specified {@link ComplexMaterial} using its {@link ResourceLocation}. - * Used to add custom entry for all instances of {@link ComplexMaterial}. - * Should be called only using Fabric "preLaunch" entry point. - * @param materialName {@link ResourceLocation} id of {@link ComplexMaterial}; - * @param entry {@link ItemEntry}. - * @see Fabric Documentation: Entrypoint - */ - public static void addItemEntry(ResourceLocation materialName, ItemEntry entry) { - List entries = ITEM_ENTRIES.get(materialName); - if (entries == null) { - entries = Lists.newArrayList(); - ITEM_ENTRIES.put(materialName, entries); - } - entries.add(entry); - } - - /** - * Adds a custom {@link RecipeEntry} for specified {@link ComplexMaterial} using its {@link ResourceLocation}. - * Used to add custom entry for all instances of {@link ComplexMaterial}. - * Should be called only using Fabric "preLaunch" entry point. - * @param materialName {@link ResourceLocation} id of {@link ComplexMaterial}; - * @param entry {@link RecipeEntry}. - * @see Fabric Documentation: Entrypoint - */ - public static void addRecipeEntry(ResourceLocation materialName, RecipeEntry entry) { - List entries = RECIPE_ENTRIES.get(materialName); - if (entries == null) { - entries = Lists.newArrayList(); - RECIPE_ENTRIES.put(materialName, entries); - } - entries.add(entry); - } - - /** - * Get all instances of all materials. - * @return {@link Collection} of {@link ComplexMaterial}. - */ - public static Collection getAllMaterials() { - return MATERIALS; - } -} diff --git a/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java b/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java deleted file mode 100644 index 710478a9..00000000 --- a/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java +++ /dev/null @@ -1,381 +0,0 @@ -package ru.bclib.complexmaterials; - -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.material.MaterialColor; -import ru.bclib.BCLib; -import ru.bclib.api.tag.*; - -import ru.bclib.blocks.BaseBarkBlock; -import ru.bclib.blocks.BaseBarrelBlock; -import ru.bclib.blocks.BaseBlock; -import ru.bclib.blocks.BaseBookshelfBlock; -import ru.bclib.blocks.BaseChestBlock; -import ru.bclib.blocks.BaseComposterBlock; -import ru.bclib.blocks.BaseCraftingTableBlock; -import ru.bclib.blocks.BaseDoorBlock; -import ru.bclib.blocks.BaseFenceBlock; -import ru.bclib.blocks.BaseGateBlock; -import ru.bclib.blocks.BaseLadderBlock; -import ru.bclib.blocks.BaseRotatedPillarBlock; -import ru.bclib.blocks.BaseSignBlock; -import ru.bclib.blocks.BaseSlabBlock; -import ru.bclib.blocks.BaseStairsBlock; -import ru.bclib.blocks.BaseStripableLogBlock; -import ru.bclib.blocks.BaseTrapdoorBlock; -import ru.bclib.blocks.BaseWoodenButtonBlock; -import ru.bclib.blocks.StripableBarkBlock; -import ru.bclib.blocks.WoodenPressurePlateBlock; -import ru.bclib.complexmaterials.entry.BlockEntry; -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) - .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) { - TagKey tagBlockLog = getBlockTag(TAG_LOGS); - TagKey tagItemLog = getItemTag(TAG_LOGS); - - addBlockEntry( - new BlockEntry(BLOCK_STRIPPED_LOG, (complexMaterial, settings) -> new BaseRotatedPillarBlock(settings)) - .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) - .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) - ); - addBlockEntry( - new BlockEntry(BLOCK_STRIPPED_BARK, (complexMaterial, settings) -> new BaseBarkBlock(settings)) - .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) - .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) - ); - - addBlockEntry( - new BlockEntry(BLOCK_LOG, (complexMaterial, settings) -> new BaseStripableLogBlock(woodColor, getBlock(BLOCK_STRIPPED_LOG))) - .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) - .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) - ); - addBlockEntry( - new BlockEntry(BLOCK_BARK, (complexMaterial, settings) -> new StripableBarkBlock(woodColor, getBlock(BLOCK_STRIPPED_BARK))) - .setBlockTags(NamedBlockTags.LOGS, NamedBlockTags.LOGS_THAT_BURN, tagBlockLog) - .setItemTags(NamedItemTags.LOGS, NamedItemTags.LOGS_THAT_BURN, tagItemLog) - ); - addBlockEntry(new BlockEntry(BLOCK_PLANKS, (complexMaterial, settings) -> new BaseBlock(settings)) - .setBlockTags(NamedBlockTags.PLANKS) - .setItemTags(NamedItemTags.PLANKS)); - - addBlockEntry(new BlockEntry(BLOCK_STAIRS, (complexMaterial, settings) -> new BaseStairsBlock(getBlock(BLOCK_PLANKS), false)) - .setBlockTags(NamedBlockTags.WOODEN_STAIRS, NamedBlockTags.STAIRS) - .setItemTags(NamedItemTags.WOODEN_STAIRS, NamedItemTags.STAIRS)); - - addBlockEntry(new BlockEntry(BLOCK_SLAB, (complexMaterial, settings) -> new BaseSlabBlock(getBlock(BLOCK_PLANKS), false)) - .setBlockTags(NamedBlockTags.WOODEN_SLABS, NamedBlockTags.SLABS) - .setItemTags(NamedItemTags.WOODEN_SLABS, NamedItemTags.SLABS)); - - addBlockEntry(new BlockEntry(BLOCK_FENCE, (complexMaterial, settings) -> new BaseFenceBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.FENCES, NamedBlockTags.WOODEN_FENCES) - .setItemTags(NamedItemTags.FENCES, NamedItemTags.WOODEN_FENCES)); - - addBlockEntry(new BlockEntry(BLOCK_GATE, (complexMaterial, settings) -> new BaseGateBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.FENCE_GATES)); - - addBlockEntry(new BlockEntry(BLOCK_BUTTON, (complexMaterial, settings) -> new BaseWoodenButtonBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.BUTTONS, NamedBlockTags.WOODEN_BUTTONS) - .setItemTags(NamedItemTags.BUTTONS, NamedItemTags.WOODEN_BUTTONS)); - - addBlockEntry(new BlockEntry(BLOCK_PRESSURE_PLATE, (complexMaterial, settings) -> new WoodenPressurePlateBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.PRESSURE_PLATES, NamedBlockTags.WOODEN_PRESSURE_PLATES) - .setItemTags(NamedItemTags.WOODEN_PRESSURE_PLATES)); - - addBlockEntry(new BlockEntry(BLOCK_TRAPDOOR, (complexMaterial, settings) -> new BaseTrapdoorBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.TRAPDOORS, NamedBlockTags.WOODEN_TRAPDOORS) - .setItemTags(NamedItemTags.TRAPDOORS, NamedItemTags.WOODEN_TRAPDOORS)); - - addBlockEntry(new BlockEntry(BLOCK_DOOR, (complexMaterial, settings) -> new BaseDoorBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.DOORS, NamedBlockTags.WOODEN_DOORS) - .setItemTags(NamedItemTags.DOORS, NamedItemTags.WOODEN_DOORS)); - - addBlockEntry(new BlockEntry(BLOCK_LADDER, (complexMaterial, settings) -> new BaseLadderBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.CLIMBABLE)); - - addBlockEntry(new BlockEntry(BLOCK_SIGN, (complexMaterial, settings) -> new BaseSignBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(NamedBlockTags.SIGNS) - .setItemTags(NamedItemTags.SIGNS)); - } - - final protected void initStorage(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) { - addBlockEntry(new BlockEntry(BLOCK_CHEST, (complexMaterial, settings) -> new BaseChestBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(CommonBlockTags.CHEST, CommonBlockTags.WOODEN_CHEST) - .setItemTags(CommonItemTags.CHEST, CommonItemTags.WOODEN_CHEST)); - - addBlockEntry(new BlockEntry(BLOCK_BARREL, (complexMaterial, settings) -> new BaseBarrelBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(CommonBlockTags.BARREL, CommonBlockTags.WOODEN_BARREL) - .setItemTags(CommonItemTags.BARREL, CommonItemTags.WOODEN_BARREL)); - } - - final protected void initDecorations(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) { - addBlockEntry(new BlockEntry(BLOCK_CRAFTING_TABLE, (complexMaterial, settings) -> new BaseCraftingTableBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(CommonBlockTags.WORKBENCHES) - .setItemTags(CommonItemTags.WORKBENCHES)); - - addBlockEntry(new BlockEntry(BLOCK_BOOKSHELF, (complexMaterial, settings) -> new BaseBookshelfBlock(getBlock(BLOCK_PLANKS))) - .setBlockTags(CommonBlockTags.BOOKSHELVES)); - - addBlockEntry(new BlockEntry(BLOCK_COMPOSTER, (complexMaterial, settings) -> 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(); - })); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/complexmaterials/entry/BlockEntry.java b/src/main/java/ru/bclib/complexmaterials/entry/BlockEntry.java deleted file mode 100644 index 3cb3a37b..00000000 --- a/src/main/java/ru/bclib/complexmaterials/entry/BlockEntry.java +++ /dev/null @@ -1,59 +0,0 @@ -package ru.bclib.complexmaterials.entry; - -import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import ru.bclib.api.tag.TagAPI; - -import ru.bclib.complexmaterials.ComplexMaterial; -import ru.bclib.registry.BlockRegistry; - -import java.util.function.BiFunction; - -public class BlockEntry extends ComplexMaterialEntry { - final BiFunction initFunction; - final boolean hasItem; - - TagKey[] blockTags; - TagKey[] itemTags; - - public BlockEntry(String suffix, BiFunction initFunction) { - this(suffix, true, initFunction); - } - - public BlockEntry(String suffix, boolean hasItem, BiFunction initFunction) { - super(suffix); - this.initFunction = initFunction; - this.hasItem = hasItem; - } - - public BlockEntry setBlockTags(TagKey... blockTags) { - this.blockTags = blockTags; - return this; - } - - public BlockEntry setItemTags(TagKey... itemTags) { - this.itemTags = itemTags; - return this; - } - - public Block init(ComplexMaterial material, FabricBlockSettings blockSettings, BlockRegistry registry) { - ResourceLocation location = getLocation(material.getModID(), material.getBaseName()); - Block block = initFunction.apply(material, blockSettings); - if (hasItem) { - registry.register(location, block); - if (itemTags != null) { - TagAPI.addItemTags(block, itemTags); - } - } - else { - registry.registerBlockOnly(location, block); - } - if (blockTags != null) { - TagAPI.addBlockTags(block, blockTags); - } - return block; - } -} diff --git a/src/main/java/ru/bclib/complexmaterials/entry/ComplexMaterialEntry.java b/src/main/java/ru/bclib/complexmaterials/entry/ComplexMaterialEntry.java deleted file mode 100644 index d2158e7f..00000000 --- a/src/main/java/ru/bclib/complexmaterials/entry/ComplexMaterialEntry.java +++ /dev/null @@ -1,40 +0,0 @@ -package ru.bclib.complexmaterials.entry; - -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public abstract class ComplexMaterialEntry { - @NotNull - private final String suffix; - - protected ComplexMaterialEntry(String suffix) { - this.suffix = suffix; - } - - public String getName(String baseName) { - return baseName + "_" + suffix; - } - - public ResourceLocation getLocation(String modID, String baseName) { - return new ResourceLocation(modID, getName(baseName)); - } - - public String getSuffix() { - return suffix; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ComplexMaterialEntry that = (ComplexMaterialEntry) o; - return suffix.equals(that.suffix); - } - - @Override - public int hashCode() { - return Objects.hash(suffix); - } -} diff --git a/src/main/java/ru/bclib/complexmaterials/entry/ItemEntry.java b/src/main/java/ru/bclib/complexmaterials/entry/ItemEntry.java deleted file mode 100644 index d1c92289..00000000 --- a/src/main/java/ru/bclib/complexmaterials/entry/ItemEntry.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.bclib.complexmaterials.entry; - -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import ru.bclib.api.tag.TagAPI; - -import ru.bclib.complexmaterials.ComplexMaterial; -import ru.bclib.registry.ItemRegistry; - -import java.util.function.BiFunction; - -public class ItemEntry extends ComplexMaterialEntry { - final BiFunction initFunction; - - TagKey[] itemTags; - - public ItemEntry(String suffix, BiFunction initFunction) { - super(suffix); - this.initFunction = initFunction; - } - - public ItemEntry setItemTags(TagKey[] itemTags) { - this.itemTags = itemTags; - return this; - } - - public Item init(ComplexMaterial material, FabricItemSettings itemSettings, ItemRegistry registry) { - ResourceLocation location = getLocation(material.getModID(), material.getBaseName()); - Item item = initFunction.apply(material, itemSettings); - registry.register(location, item); - if (itemTags != null) { - TagAPI.addItemTags(item, itemTags); - } - return item; - } -} diff --git a/src/main/java/ru/bclib/complexmaterials/entry/RecipeEntry.java b/src/main/java/ru/bclib/complexmaterials/entry/RecipeEntry.java deleted file mode 100644 index d3c08420..00000000 --- a/src/main/java/ru/bclib/complexmaterials/entry/RecipeEntry.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.bclib.complexmaterials.entry; - -import net.minecraft.resources.ResourceLocation; -import ru.bclib.complexmaterials.ComplexMaterial; -import ru.bclib.config.PathConfig; -import ru.bclib.interfaces.TriConsumer; - -public class RecipeEntry extends ComplexMaterialEntry { - final TriConsumer initFunction; - - public RecipeEntry(String suffix, TriConsumer initFunction) { - super(suffix); - this.initFunction = initFunction; - } - - public void init(ComplexMaterial material, PathConfig recipeConfig) { - initFunction.accept(material, recipeConfig, getLocation(material.getModID(), material.getBaseName())); - } -} diff --git a/src/main/java/ru/bclib/config/CategoryConfig.java b/src/main/java/ru/bclib/config/CategoryConfig.java deleted file mode 100644 index 54611ce1..00000000 --- a/src/main/java/ru/bclib/config/CategoryConfig.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.bclib.config; - -public class CategoryConfig extends IdConfig { - - public CategoryConfig(String modID, String group) { - super(modID, group, (id, category) -> { - return new ConfigKey(id.getPath(), id.getNamespace(), category); - }); - } -} diff --git a/src/main/java/ru/bclib/config/ClientConfig.java b/src/main/java/ru/bclib/config/ClientConfig.java deleted file mode 100644 index e0bcd570..00000000 --- a/src/main/java/ru/bclib/config/ClientConfig.java +++ /dev/null @@ -1,68 +0,0 @@ -package ru.bclib.config; - -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.handler.autosync.AutoSync; - -public class ClientConfig extends NamedPathConfig { - public static final ConfigToken SUPPRESS_EXPERIMENTAL_DIALOG = ConfigToken.Boolean(false, "suppressExperimentalDialogOnLoad", "ui"); - - @ConfigUI(topPadding = 12) - public static final ConfigToken ENABLED = ConfigToken.Boolean(true, "enabled", AutoSync.SYNC_CATEGORY); - - @ConfigUI(leftPadding = 8) - public static final DependendConfigToken ACCEPT_CONFIGS = DependendConfigToken.Boolean(true, "acceptConfigs", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - @ConfigUI(leftPadding = 8) - public static final DependendConfigToken ACCEPT_FILES = DependendConfigToken.Boolean(true, "acceptFiles", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - @ConfigUI(leftPadding = 8) - public static final DependendConfigToken ACCEPT_MODS = DependendConfigToken.Boolean(false, "acceptMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - @ConfigUI(leftPadding = 8) - public static final DependendConfigToken DISPLAY_MOD_INFO = DependendConfigToken.Boolean(true, "displayModInfo", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - - @ConfigUI(topPadding = 12) - public static final ConfigToken DEBUG_HASHES = ConfigToken.Boolean(false, "debugHashes", AutoSync.SYNC_CATEGORY); - - @ConfigUI(leftPadding = 8) - public static final ConfigToken CUSTOM_FOG_RENDERING = ConfigToken.Boolean(true, "customFogRendering", "rendering"); - @ConfigUI(leftPadding = 8) - public static final ConfigToken NETHER_THICK_FOG = ConfigToken.Boolean(true, "netherThickFog", "rendering"); - - public ClientConfig() { - super(BCLib.MOD_ID, "client", false); - } - - public boolean shouldPrintDebugHashes() { - return get(DEBUG_HASHES); - } - - public boolean isAllowingAutoSync() { - return get(ENABLED); - } - - public boolean isAcceptingMods() { - return get(ACCEPT_MODS) /*&& isAllowingAutoSync()*/; - } - - public boolean isAcceptingConfigs() { - return get(ACCEPT_CONFIGS) /*&& isAllowingAutoSync()*/; - } - - public boolean isAcceptingFiles() { - return get(ACCEPT_FILES) /*&& isAllowingAutoSync()*/; - } - - public boolean isShowingModInfo() { - return get(DISPLAY_MOD_INFO) /*&& isAllowingAutoSync()*/; - } - - public boolean suppressExperimentalDialog() { - return get(SUPPRESS_EXPERIMENTAL_DIALOG); - } - - public boolean netherThickFog() { - return get(NETHER_THICK_FOG); - } - - public boolean renderCustomFog() { - return get(CUSTOM_FOG_RENDERING); - } -} diff --git a/src/main/java/ru/bclib/config/Config.java b/src/main/java/ru/bclib/config/Config.java deleted file mode 100644 index a883116e..00000000 --- a/src/main/java/ru/bclib/config/Config.java +++ /dev/null @@ -1,242 +0,0 @@ -package ru.bclib.config; - -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.dataexchange.SyncFileHash; -import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID; -import ru.bclib.api.dataexchange.handler.autosync.FileContentWrapper; -import ru.bclib.config.ConfigKeeper.BooleanEntry; -import ru.bclib.config.ConfigKeeper.Entry; -import ru.bclib.config.ConfigKeeper.FloatEntry; -import ru.bclib.config.ConfigKeeper.IntegerEntry; -import ru.bclib.config.ConfigKeeper.RangeEntry; -import ru.bclib.config.ConfigKeeper.StringArrayEntry; -import ru.bclib.config.ConfigKeeper.StringEntry; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public abstract class Config { - protected final static Map AUTO_SYNC_CONFIGS = new HashMap<>(); - public static final String CONFIG_SYNC_PREFIX = "CONFIG_"; - protected final ConfigKeeper keeper; - protected final boolean autoSync; - public final String configID; - - protected abstract void registerEntries(); - - protected Config(String modID, String group) { - this(modID, group, true, false); - } - - protected Config(String modID, String group, boolean autoSync) { - this(modID, group, autoSync, false); - } - - protected Config(String modID, String group, boolean autoSync, boolean diffContent) { - configID = modID + "." + group; - this.keeper = new ConfigKeeper(modID, group); - this.registerEntries(); - this.autoSync = autoSync; - - if (autoSync) { - final String uid = CONFIG_SYNC_PREFIX + configID; - final AutoSyncID aid = new AutoSyncID(BCLib.MOD_ID, uid); - if (diffContent) - DataExchangeAPI.addAutoSyncFile(aid.modID, aid.uniqueID, keeper.getConfigFile(),this::compareForSync); - else - DataExchangeAPI.addAutoSyncFile(aid.modID, aid.uniqueID, keeper.getConfigFile()); - - AUTO_SYNC_CONFIGS.put(aid, this); - BCLib.LOGGER.info("Added Config " + configID + " to auto sync (" + (diffContent?"content diff":"file hash") + ")"); - } - } - - private boolean compareForSync(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content) { - //identical hashes => nothing to do - if (clientHash.equals(serverHash)) { - return false; - } - - return keeper.compareAndUpdateForSync(content); - } - - public void saveChanges() { - this.keeper.save(); - } - - public static void reloadSyncedConfig(AutoSyncID aid, File file) { - Config cfg = AUTO_SYNC_CONFIGS.get(aid); - if (cfg != null) { - cfg.reload(); - } - } - - public void reload() { - this.keeper.reload(); - BCLib.LOGGER.info("Did Reload " + keeper.getConfigFile()); - } - - @Nullable - public > E getEntry(ConfigKey key, Class type) { - return this.keeper.getEntry(key, type); - } - - @Nullable - public > T getDefault(ConfigKey key, Class type) { - Entry entry = keeper.getEntry(key, type); - return entry != null ? entry.getDefault() : null; - } - - protected String getString(ConfigKey key, String defaultValue) { - String str = keeper.getValue(key, StringEntry.class); - if (str == null) { - StringEntry entry = keeper.registerEntry(key, new StringEntry(defaultValue)); - return entry.getValue(); - } - return str != null ? str : defaultValue; - } - - protected String getString(ConfigKey key) { - String str = keeper.getValue(key, StringEntry.class); - return str != null ? str : ""; - } - - protected boolean setString(ConfigKey key, String value) { - try { - StringEntry entry = keeper.getEntry(key, StringEntry.class); - if (entry == null) return false; - entry.setValue(value); - return true; - } - catch (NullPointerException ex) { - BCLib.LOGGER.catching(ex); - } - return false; - } - - protected int getInt(ConfigKey key, int defaultValue) { - Integer val = keeper.getValue(key, IntegerEntry.class); - if (val == null) { - IntegerEntry entry = keeper.registerEntry(key, new IntegerEntry(defaultValue)); - return entry.getValue(); - } - return val != null ? val : defaultValue; - } - - protected int getInt(ConfigKey key) { - Integer val = keeper.getValue(key, IntegerEntry.class); - return val != null ? val : 0; - } - - protected boolean setInt(ConfigKey key, int value) { - try { - IntegerEntry entry = keeper.getEntry(key, IntegerEntry.class); - if (entry == null) return false; - entry.setValue(value); - return true; - } - catch (NullPointerException ex) { - BCLib.LOGGER.catching(ex); - } - return false; - } - - protected , RE extends RangeEntry> boolean setRanged(ConfigKey key, T value, Class type) { - try { - RangeEntry entry = keeper.getEntry(key, type); - if (entry == null) return false; - entry.setValue(value); - return true; - } - catch (NullPointerException | ClassCastException ex) { - BCLib.LOGGER.catching(ex); - } - return false; - } - - protected float getFloat(ConfigKey key, float defaultValue) { - Float val = keeper.getValue(key, FloatEntry.class); - if (val == null) { - FloatEntry entry = keeper.registerEntry(key, new FloatEntry(defaultValue)); - return entry.getValue(); - } - return val; - } - - protected float getFloat(ConfigKey key) { - Float val = keeper.getValue(key, FloatEntry.class); - return val != null ? val : 0.0F; - } - - protected boolean setFloat(ConfigKey key, float value) { - try { - FloatEntry entry = keeper.getEntry(key, FloatEntry.class); - if (entry == null) return false; - entry.setValue(value); - return true; - } - catch (NullPointerException ex) { - BCLib.LOGGER.catching(ex); - } - return false; - } - - protected boolean getBoolean(ConfigKey key, boolean defaultValue) { - Boolean val = keeper.getValue(key, BooleanEntry.class); - if (val == null) { - BooleanEntry entry = keeper.registerEntry(key, new BooleanEntry(defaultValue)); - return entry.getValue(); - } - return val; - } - - protected boolean getBoolean(ConfigKey key) { - Boolean val = keeper.getValue(key, BooleanEntry.class); - return val != null ? val : false; - } - - protected boolean setBoolean(ConfigKey key, boolean value) { - try { - BooleanEntry entry = keeper.getEntry(key, BooleanEntry.class); - if (entry == null) return false; - entry.setValue(value); - return true; - } - catch (NullPointerException ex) { - BCLib.LOGGER.catching(ex); - } - return false; - } - - protected List getStringArray(ConfigKey key, List defaultValue) { - List str = keeper.getValue(key, StringArrayEntry.class); - if (str == null) { - StringArrayEntry entry = keeper.registerEntry(key, new StringArrayEntry(defaultValue)); - return entry.getValue(); - } - return str != null ? str : defaultValue; - } - - protected List getStringArray(ConfigKey key) { - List str = keeper.getValue(key, StringArrayEntry.class); - return str != null ? str : new ArrayList<>(0); - } - - protected boolean setStringArray(ConfigKey key, List value) { - try { - StringArrayEntry entry = keeper.getEntry(key, StringArrayEntry.class); - if (entry == null) return false; - entry.setValue(value); - return true; - } - catch (NullPointerException ex) { - BCLib.LOGGER.catching(ex); - } - return false; - } -} diff --git a/src/main/java/ru/bclib/config/ConfigKeeper.java b/src/main/java/ru/bclib/config/ConfigKeeper.java deleted file mode 100644 index 32936e14..00000000 --- a/src/main/java/ru/bclib/config/ConfigKeeper.java +++ /dev/null @@ -1,459 +0,0 @@ -package ru.bclib.config; - -import com.google.common.collect.Maps; -import com.google.common.reflect.TypeToken; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.minecraft.util.GsonHelper; -import org.jetbrains.annotations.Nullable; -import ru.bclib.api.dataexchange.handler.autosync.FileContentWrapper; -import ru.bclib.util.JsonFactory; -import ru.bclib.util.Pair; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.OutputStream; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public final class ConfigKeeper { - private final Map> configEntries = Maps.newHashMap(); - private final ConfigWriter writer; - - private JsonObject configObject; - private boolean changed = false; - - public ConfigKeeper(String modID, String group) { - this.writer = new ConfigWriter(modID, group); - this.configObject = writer.load(); - } - - public File getConfigFile() { - return this.writer.getConfigFile(); - } - - boolean compareAndUpdateForSync(FileContentWrapper content) { - ByteArrayInputStream inputStream = content.getInputStream(); - final JsonObject other = JsonFactory.getJsonObject(inputStream); - - boolean changed = this.compareAndUpdateForSync(other); - if (changed) { - OutputStream outStream = content.getEmptyOutputStream(); - JsonFactory.storeJson(outStream, this.configObject); - content.syncWithOutputStream(); - } - return changed; - } - - boolean compareAndUpdateForSync(JsonObject other) { - return compareAndUpdateForSync(this.configObject, other); - } - - private static Pair> find(JsonObject json, Pair key) { - for (var entry : json.entrySet()) { - final Pair otherKey = ConfigKey.realKey(entry.getKey()); - if (otherKey.first.equals(key.first)) return new Pair<>(entry.getValue(), otherKey); - } - - return null; - } - - /** - * Called for content based auto-sync. - * - * @param me - When called in AutoSync this represents the content of the client. - * @param other - When called in AutoSync, this represents the content of the server - * @return {@code true} if content was changed - */ - static boolean compareAndUpdateForSync(JsonObject me, JsonObject other) { - boolean changed = false; - for (var otherEntry : other.entrySet()) { - final Pair otherKey = ConfigKey.realKey(otherEntry.getKey()); - final JsonElement otherValue = otherEntry.getValue(); - - Pair> temp = find(me, otherKey); - //we already have an entry - if (temp != null) { - final Pair myKey = temp.second; - final JsonElement myValue = temp.first; - - if ((otherValue.isJsonNull() && !myValue.isJsonNull()) || (otherValue.isJsonPrimitive() && !myValue.isJsonPrimitive()) || (otherValue.isJsonObject() && !myValue.isJsonObject()) || (otherValue.isJsonArray() && !myValue.isJsonArray())) { - //types are different => replace with "server"-version in other - changed = true; - me.add(myKey.first + myKey.second, otherValue); - } - else if (otherValue.isJsonPrimitive() || otherValue.isJsonArray() || otherValue.isJsonNull()) { - if (!otherValue.equals(myValue)) { - changed = true; - me.add(myKey.first + myKey.second, otherValue); - } - } - else if (otherValue.isJsonObject()) { - changed |= compareAndUpdateForSync(myValue.getAsJsonObject(), otherValue.getAsJsonObject()); - } - } - else { //no entry, just copy the value from other - if (!otherValue.isJsonNull()) { - changed = true; - temp = find(me, otherKey); - me.add(otherKey.first + otherKey.second, otherValue); - } - } - } - - return changed; - } - - - public void save() { - if (!changed) return; - this.writer.save(); - this.changed = false; - } - - void reload() { - this.configObject = this.writer.reload(); - this.configEntries.clear(); - this.changed = false; - } - - private > void initializeEntry(ConfigKey key, E entry) { - if (configObject == null) { - return; - } - String[] path = key.getPath(); - JsonObject obj = configObject; - - if (!key.isRoot()) { - for (String group : path) { - JsonElement element = obj.get(group); - if (element == null || !element.isJsonObject()) { - element = new JsonObject(); - obj.add(group, element); - } - obj = element.getAsJsonObject(); - } - } - - String paramKey = key.getEntry(); - if (entry.hasDefaultInName()) { - paramKey += " [default: " + entry.getDefault() + "]"; - } - - this.changed |= entry.setLocation(obj, paramKey); - } - - private > void storeValue(E entry, T value) { - if (configObject == null) { - return; - } - T val = entry.getValue(); - if (value.equals(val)) return; - entry.toJson(value); - this.changed = true; - } - - private > T getValue(E entry) { - if (!entry.hasLocation()) { - return entry.getDefault(); - } - return entry.fromJson(); - } - - @Nullable - public > E getEntry(ConfigKey key, Class type) { - Entry entry = this.configEntries.get(key); - if (type.isInstance(entry)) { - return type.cast(entry); - } - return null; - } - - @Nullable - public > T getValue(ConfigKey key, Class type) { - Entry entry = this.getEntry(key, type); - if (entry == null) { - return null; - } - return entry.getValue(); - } - - public > E registerEntry(ConfigKey key, E entry) { - entry.setWriter(value -> this.storeValue(entry, value)); - entry.setReader(() -> { - return this.getValue(entry); - }); - this.initializeEntry(key, entry); - this.configEntries.put(key, entry); - return entry; - } - - public static class BooleanEntry extends Entry { - - public BooleanEntry(Boolean defaultValue) { - super(defaultValue); - } - - @Override - public Boolean fromJson() { - return GsonHelper.getAsBoolean(location, key, defaultValue); - } - - @Override - public void toJson(Boolean value) { - this.location.addProperty(key, value); - } - } - - public static class FloatEntry extends Entry { - - public FloatEntry(Float defaultValue) { - super(defaultValue); - } - - @Override - public Float fromJson() { - return GsonHelper.getAsFloat(location, key, defaultValue); - } - - @Override - public void toJson(Float value) { - this.location.addProperty(key, value); - } - } - - public static class FloatRange extends RangeEntry { - - public FloatRange(Float defaultValue, float minVal, float maxVal) { - super(defaultValue, minVal, maxVal); - } - - @Override - public Float fromJson() { - return GsonHelper.getAsFloat(location, key, defaultValue); - } - - @Override - public void toJson(Float value) { - this.location.addProperty(key, value); - } - } - - public static class IntegerEntry extends Entry { - - public IntegerEntry(Integer defaultValue) { - super(defaultValue); - } - - @Override - public Integer getDefault() { - return this.defaultValue; - } - - @Override - public Integer fromJson() { - return GsonHelper.getAsInt(location, key, defaultValue); - } - - @Override - public void toJson(Integer value) { - this.location.addProperty(key, value); - } - } - - public static class IntegerRange extends RangeEntry { - - public IntegerRange(Integer defaultValue, int minVal, int maxVal) { - super(defaultValue, minVal, maxVal); - } - - @Override - public Integer fromJson() { - return GsonHelper.getAsInt(location, key, defaultValue); - } - - @Override - public void toJson(Integer value) { - this.location.addProperty(key, value); - } - } - - public static class StringEntry extends Entry { - - public StringEntry(String defaultValue) { - super(defaultValue); - } - - @Override - public String fromJson() { - return GsonHelper.getAsString(location, key, defaultValue); - } - - @Override - public void toJson(String value) { - this.location.addProperty(key, value); - } - } - - public static abstract class ArrayEntry extends Entry> { - public ArrayEntry(List defaultValue) { - super(defaultValue); - } - - protected abstract T getValue(JsonElement element); - protected abstract void add(JsonArray array, T element); - - private JsonArray toArray(List input){ - final JsonArray array = new JsonArray(); - input.forEach(s -> add(array, s)); - return array; - } - - @Override - public List fromJson() { - final JsonArray resArray = GsonHelper.getAsJsonArray(location, key, toArray(defaultValue)); - final List res = new ArrayList<>(resArray.size()); - resArray.forEach(e -> res.add(getValue(e))); - - return res; - } - - @Override - public void toJson(List value) { - this.location.add(key, toArray(value)); - } - } - - public static class StringArrayEntry extends ArrayEntry { - public StringArrayEntry(List defaultValue) { - super(defaultValue); - } - - @Override - protected String getValue(JsonElement el){ - return el.getAsString(); - } - - protected void add(JsonArray array, String el){ - array.add(el); - } - - @Override - protected boolean hasDefaultInName() { - return false; - } - } - - public static class EnumEntry> extends Entry { - private final Type type; - - public EnumEntry(T defaultValue) { - super(defaultValue); - TypeToken token = new TypeToken() { - private static final long serialVersionUID = 1L; - }; - this.type = token.getType(); - } - - @Override - public T getDefault() { - return this.defaultValue; - } - - @Override - public T fromJson() { - return JsonFactory.GSON.fromJson(location.get(key), type); - } - - @Override - public void toJson(T value) { - location.addProperty(key, JsonFactory.GSON.toJson(value, type)); - } - } - - public static abstract class RangeEntry> extends Entry { - private final T min, max; - - public RangeEntry(T defaultValue, T minVal, T maxVal) { - super(defaultValue); - this.min = minVal; - this.max = maxVal; - } - - @Override - public void setValue(T value) { - super.setValue(value.compareTo(min) < 0 ? min : value.compareTo(max) > 0 ? max : value); - } - - public T minValue() { - return this.min; - } - - public T maxValue() { - return this.max; - } - } - - public static abstract class Entry { - protected final T defaultValue; - protected Consumer writer; - protected Supplier reader; - protected JsonObject location; - protected String key; - - public abstract T fromJson(); - - public abstract void toJson(T value); - - public Entry(T defaultValue) { - this.defaultValue = defaultValue; - } - - protected void setWriter(Consumer writer) { - this.writer = writer; - } - - protected void setReader(Supplier reader) { - this.reader = reader; - } - - protected boolean setLocation(JsonObject location, String key) { - this.location = location; - this.key = key; - if (!location.has(key)) { - this.toJson(defaultValue); - return true; - } - return false; - } - - protected boolean hasLocation() { - return this.location != null && this.key != null; - } - - public T getValue() { - return this.reader.get(); - } - - public void setValue(T value) { - this.writer.accept(value); - } - - public T getDefault() { - return this.defaultValue; - } - - public void setDefault() { - this.setValue(defaultValue); - } - - protected boolean hasDefaultInName() { - return true; - } - } -} diff --git a/src/main/java/ru/bclib/config/ConfigKey.java b/src/main/java/ru/bclib/config/ConfigKey.java deleted file mode 100644 index 5a1663c8..00000000 --- a/src/main/java/ru/bclib/config/ConfigKey.java +++ /dev/null @@ -1,100 +0,0 @@ -package ru.bclib.config; - -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; -import ru.bclib.util.Pair; - -import java.util.Arrays; - -public class ConfigKey { - private final String path[]; - private final String entry; - private final boolean root; - - public ConfigKey(String entry, String... path) { - this.validate(entry); - this.path = path; - this.entry = entry; - this.root = path.length == 0 || (path.length == 1 && path[0].isEmpty()); - } - - public ConfigKey(String entry, ResourceLocation path) { - this(entry, path.getNamespace(), path.getPath()); - } - - public String[] getPath() { - return path; - } - - public String getEntry() { - return entry; - } - - public boolean isRoot() { - return root; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(path); - result = prime * result + entry.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ConfigKey)) { - return false; - } - ConfigKey other = (ConfigKey) obj; - if (other.path.length != path.length) { - return false; - } - for (int i = 0; i < path.length; i++) { - if (!path[i].equals(other.path[i])) { - return false; - } - } - if (!entry.equals(other.entry)) { - return false; - } - return true; - } - - @Override - public String toString() { - if (root) { - return String.format("[root]:%s", entry); - } - String p = path[0]; - for (int i = 1; i < path.length; i++) { - p += "." + path[i]; - } - return String.format("%s:%s", p, entry); - } - - private void validate(String entry) { - if (entry == null) { - throw new NullPointerException("Config key must be not null!"); - } - if (entry.isEmpty()) { - throw new IndexOutOfBoundsException("Config key must be not empty!"); - } - } - - public static Pair realKey(@NotNull String key) { - String[] parts = key.split("\\[default:", 2); - if (parts.length == 1) { - return new Pair(parts[0].trim(), ""); - } - else if (parts.length == 2) { - return new Pair(parts[0].trim(), " " + ("[default:" + parts[1]).trim()); - } - return new Pair(key, ""); - } -} diff --git a/src/main/java/ru/bclib/config/ConfigUI.java b/src/main/java/ru/bclib/config/ConfigUI.java deleted file mode 100644 index b91975bc..00000000 --- a/src/main/java/ru/bclib/config/ConfigUI.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.config; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD}) -public @interface ConfigUI { - /** - * When {@code true}, this option will not generate UI-Elements. - */ - public boolean hide() default false; - - /** - * When a Widget is generated for this option, it will be indented by this Value - */ - public int leftPadding() default 0; - - /** - * When a Widget is generated for this option, it will be indented by this Value - */ - public int topPadding() default 0; -} diff --git a/src/main/java/ru/bclib/config/ConfigWriter.java b/src/main/java/ru/bclib/config/ConfigWriter.java deleted file mode 100644 index 5a383ccc..00000000 --- a/src/main/java/ru/bclib/config/ConfigWriter.java +++ /dev/null @@ -1,71 +0,0 @@ -package ru.bclib.config; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.fabricmc.loader.api.FabricLoader; -import ru.bclib.util.JsonFactory; - -import java.io.File; -import java.nio.file.Path; - -public class ConfigWriter { - private final static Path GAME_CONFIG_DIR = FabricLoader.getInstance().getConfigDir(); - - private final File configFile; - private JsonObject configObject; - - public ConfigWriter(String modID, String configFile) { - this.configFile = new File(GAME_CONFIG_DIR.resolve(modID).toFile() , configFile + ".json"); - File parent = this.configFile.getParentFile(); - if (!parent.exists()) { - parent.mkdirs(); - } - this.load(); - } - - File getConfigFile(){ - return this.configFile; - } - - public JsonObject getConfig() { - return configObject; - } - - public void save() { - if (configObject == null) { - return; - } - save(configFile, configObject); - } - - JsonObject reload() { - configObject = load(configFile); - return configObject; - } - - public JsonObject load() { - if (configObject == null) { - configObject = load(configFile); - } - return configObject; - } - - public void save(JsonElement config) { - this.configObject = config.getAsJsonObject(); - save(configFile, config); - } - - public static JsonObject load(File configFile) { - return JsonFactory.getJsonObject(configFile); - } - - public static void save(File configFile, JsonElement config) { - JsonFactory.storeJson(configFile, config); - } - - public static String scrubFileName(String input) { - input = input.replaceAll("[/\\ ]+", "_"); - input = input.replaceAll("[,:&\"\\|\\<\\>\\?\\*]", "_"); - return input; - } -} diff --git a/src/main/java/ru/bclib/config/Configs.java b/src/main/java/ru/bclib/config/Configs.java deleted file mode 100644 index 4c124468..00000000 --- a/src/main/java/ru/bclib/config/Configs.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.bclib.config; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import ru.bclib.BCLib; -import ru.bclib.config.ConfigKeeper.StringArrayEntry; - -import java.util.Collections; - -public class Configs { - // Client and Server-Config must be the first entries. They are not part of the Auto-Sync process - // But will be needed by other Auto-Sync Config-Files - @Environment(EnvType.CLIENT) - public static final ClientConfig CLIENT_CONFIG = new ClientConfig(); - public static final ServerConfig SERVER_CONFIG = new ServerConfig(); - - public static final GeneratorConfig GENERATOR_CONFIG = new GeneratorConfig(); - public static final MainConfig MAIN_CONFIG = new MainConfig(); - - public static final PathConfig RECIPE_CONFIG = new PathConfig(BCLib.MOD_ID, "recipes"); - public static final PathConfig BIOMES_CONFIG = new PathConfig(BCLib.MOD_ID, "biomes", false); - - public static final String MAIN_PATCH_CATEGORY = "patches"; - - public static void save() { - MAIN_CONFIG.saveChanges(); - RECIPE_CONFIG.saveChanges(); - GENERATOR_CONFIG.saveChanges(); - BIOMES_CONFIG.saveChanges(); - } - - static { - BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("end_land_biomes", "force_include"), new StringArrayEntry(Collections.EMPTY_LIST)); - BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("end_void_biomes", "force_include"), new StringArrayEntry(Collections.EMPTY_LIST)); - BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("nether_biomes", "force_include"), new StringArrayEntry(Collections.EMPTY_LIST)); - BIOMES_CONFIG.keeper.registerEntry(new ConfigKey("nether_biomes", "force_exclude"), new StringArrayEntry(Collections.EMPTY_LIST)); - } -} diff --git a/src/main/java/ru/bclib/config/EntryConfig.java b/src/main/java/ru/bclib/config/EntryConfig.java deleted file mode 100644 index 0b22de86..00000000 --- a/src/main/java/ru/bclib/config/EntryConfig.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.config; - -public class EntryConfig extends IdConfig { - public EntryConfig(String modID, String group) { - super(modID, group, (id, entry) -> { - return new ConfigKey(entry, id); - }); - } -} diff --git a/src/main/java/ru/bclib/config/GeneratorConfig.java b/src/main/java/ru/bclib/config/GeneratorConfig.java deleted file mode 100644 index cf46cfe0..00000000 --- a/src/main/java/ru/bclib/config/GeneratorConfig.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.bclib.config; - -import ru.bclib.BCLib; - -public class GeneratorConfig extends NamedPathConfig { - public static final ConfigToken USE_OLD_GENERATOR = ConfigToken.Boolean(false, "useOldBiomeGenerator", "options"); - - - public GeneratorConfig() { - super(BCLib.MOD_ID, "generator", false); - } - - public boolean useOldGenerator() { - return get(USE_OLD_GENERATOR); - } -} diff --git a/src/main/java/ru/bclib/config/IdConfig.java b/src/main/java/ru/bclib/config/IdConfig.java deleted file mode 100644 index cc8f8330..00000000 --- a/src/main/java/ru/bclib/config/IdConfig.java +++ /dev/null @@ -1,91 +0,0 @@ -package ru.bclib.config; - -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; -import ru.bclib.config.ConfigKeeper.Entry; -import ru.bclib.config.ConfigKeeper.FloatRange; -import ru.bclib.config.ConfigKeeper.IntegerRange; - -import java.util.function.BiFunction; - -public class IdConfig extends Config { - protected final BiFunction keyFactory; - - public IdConfig(String modID, String group, BiFunction keyFactory) { - super(modID, group); - this.keyFactory = keyFactory; - } - - @Override - protected void registerEntries() {} - - protected ConfigKey createKey(ResourceLocation id, String key) { - return this.keyFactory.apply(id, key); - } - - @Nullable - public > E getEntry(ResourceLocation id, String key, Class type) { - return this.getEntry(createKey(id, key), type); - } - - @Nullable - public > T getDefault(ResourceLocation id, String key, Class type) { - return this.getDefault(createKey(id, key), type); - } - - public String getString(ResourceLocation id, String key, String defaultValue) { - return this.getString(createKey(id, key), defaultValue); - } - - public String getString(ResourceLocation id, String key) { - return this.getString(createKey(id, key)); - } - - public boolean setString(ResourceLocation id, String key, String value) { - return this.setString(createKey(id, key), value); - } - - public int getInt(ResourceLocation id, String key, int defaultValue) { - return this.getInt(createKey(id, key), defaultValue); - } - - public int getInt(ResourceLocation id, String key) { - return this.getInt(createKey(id, key)); - } - - public boolean setInt(ResourceLocation id, String key, int value) { - return this.setInt(createKey(id, key), value); - } - - public boolean setRangedInt(ResourceLocation id, String key, int value) { - return this.setRanged(createKey(id, key), value, IntegerRange.class); - } - - public boolean setRangedFloat(ResourceLocation id, String key, float value) { - return this.setRanged(createKey(id, key), value, FloatRange.class); - } - - public float getFloat(ResourceLocation id, String key, float defaultValue) { - return this.getFloat(createKey(id, key), defaultValue); - } - - public float getFloat(ResourceLocation id, String key) { - return this.getFloat(createKey(id, key)); - } - - public boolean setFloat(ResourceLocation id, String key, float value) { - return this.setFloat(createKey(id, key), value); - } - - public boolean getBoolean(ResourceLocation id, String key, boolean defaultValue) { - return this.getBoolean(createKey(id, key), defaultValue); - } - - public boolean getBoolean(ResourceLocation id, String key) { - return this.getBoolean(createKey(id, key)); - } - - public boolean setBoolean(ResourceLocation id, String key, boolean value) { - return this.setBoolean(createKey(id, key), value); - } -} diff --git a/src/main/java/ru/bclib/config/MainConfig.java b/src/main/java/ru/bclib/config/MainConfig.java deleted file mode 100644 index 11b92416..00000000 --- a/src/main/java/ru/bclib/config/MainConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.config; - -import ru.bclib.BCLib; - -public class MainConfig extends NamedPathConfig { - public static final ConfigToken APPLY_PATCHES = ConfigToken.Boolean(true, "applyPatches", Configs.MAIN_PATCH_CATEGORY); - - @ConfigUI(leftPadding = 8) - public static final ConfigToken REPAIR_BIOMES = DependendConfigToken.Boolean(true, "repairBiomesOnLoad", Configs.MAIN_PATCH_CATEGORY, (config) -> config.get(APPLY_PATCHES)); - - public MainConfig() { - super(BCLib.MOD_ID, "main", true, true); - } - - public boolean applyPatches() { - return get(APPLY_PATCHES); - } - - public boolean repairBiomes() { - return get(REPAIR_BIOMES); - } -} diff --git a/src/main/java/ru/bclib/config/NamedPathConfig.java b/src/main/java/ru/bclib/config/NamedPathConfig.java deleted file mode 100644 index b7ce4404..00000000 --- a/src/main/java/ru/bclib/config/NamedPathConfig.java +++ /dev/null @@ -1,250 +0,0 @@ -package ru.bclib.config; - -import net.minecraft.resources.ResourceLocation; -import ru.bclib.BCLib; -import ru.bclib.config.ConfigKeeper.BooleanEntry; -import ru.bclib.config.ConfigKeeper.FloatEntry; -import ru.bclib.config.ConfigKeeper.IntegerEntry; -import ru.bclib.config.ConfigKeeper.StringArrayEntry; -import ru.bclib.config.ConfigKeeper.StringEntry; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.LinkedList; -import java.util.List; -import java.util.function.Predicate; - -public class NamedPathConfig extends PathConfig{ - public static class ConfigTokenDescription { - public final ConfigToken token; - public final String internalName; - public final Boolean hidden; - public final int leftPadding; - public final int topPadding; - - @SuppressWarnings("unchecked") - ConfigTokenDescription(Field fl) throws IllegalAccessException{ - token = (ConfigToken) fl.get(null); - internalName = fl.getName(); - - ConfigUI ui = fl.getAnnotation(ConfigUI.class); - if (ui!=null) { - this.hidden = ui.hide(); - leftPadding = ui.leftPadding(); - topPadding = ui.topPadding(); - } else { - this.hidden = false; - this.leftPadding = 0; - topPadding = 0; - } - - } - - public String getPath(){ - StringBuilder path = new StringBuilder(); - for (String p : token.getPath()){ - path.append(".") - .append(p); - - } - path.append(".").append(token.getEntry()); - return path.toString(); - } - } - public static class DependendConfigToken extends ConfigToken{ - protected final Predicate dependenciesTrue; - - protected DependendConfigToken(Class type, T defaultValue, String entry, ResourceLocation path, Predicate dependenciesTrue) { - this(type, defaultValue, entry, new String[]{path.getNamespace(), path.getPath()}, dependenciesTrue); - } - - protected DependendConfigToken(Class type, T defaultValue, String entry, String path, Predicate dependenciesTrue) { - super(type, defaultValue, entry, path); - this.dependenciesTrue = dependenciesTrue; - } - - protected DependendConfigToken(Class type, T defaultValue, String entry, String[] path, Predicate dependenciesTrue) { - super(type, defaultValue, entry, path); - this.dependenciesTrue = dependenciesTrue; - } - - public boolean dependenciesTrue(NamedPathConfig config){ - return dependenciesTrue.test(config); - } - - public static DependendConfigToken Boolean(boolean defaultValue, String entry, String path, Predicate dependenciesTrue) { - return new DependendConfigToken(BooleanEntry.class, defaultValue, entry, path, dependenciesTrue); - } - } - - public static class ConfigToken extends ConfigKey{ - public final T defaultValue; - public final Class type; - - protected ConfigToken(Class type, T defaultValue, String entry, ResourceLocation path) { - this(type, defaultValue, entry, path.getNamespace(), path.getPath()); - } - - @SuppressWarnings("unchecked") - protected ConfigToken(Class type, T defaultValue, String entry, String... path) { - super(entry, path); - this.defaultValue = defaultValue; - - this.type = type; - } - - public boolean dependenciesTrue(NamedPathConfig config){ - return true; - } - - public static ConfigToken Boolean(boolean defaultValue, String entry, String path) { - return new ConfigToken(BooleanEntry.class, defaultValue, entry, path); - } - - public static ConfigToken Int(int defaultValue, String entry, String path) { - return new ConfigToken(IntegerEntry.class, defaultValue, entry, path); - } - - public static ConfigToken Float(float defaultValue, String entry, String path) { - return new ConfigToken(FloatEntry.class, defaultValue, entry, path); - } - - public static ConfigToken String(String defaultValue, String entry, String path) { - return new ConfigToken(StringEntry.class, defaultValue, entry, path); - } - - public static ConfigToken> StringArray(List defaultValue, String entry, String path) { - return new ConfigToken>(StringArrayEntry.class, defaultValue, entry, path); - } - } - - public NamedPathConfig(String modID, String group, boolean autoSync, boolean diffContent) { - super(modID, group, autoSync, diffContent); - onInit(); - } - - public NamedPathConfig(String modID, String group, boolean autoSync) { - super(modID, group, autoSync); - onInit(); - } - - public NamedPathConfig(String modID, String group) { - super(modID, group); - onInit(); - } - - public List> getAllOptions(){ - List> res = new LinkedList<>(); - for (Field fl : this.getClass().getDeclaredFields()){ - int modifiers = fl.getModifiers(); - if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && ConfigToken.class.isAssignableFrom(fl.getType())) { - try { - res.add(new ConfigTokenDescription<>(fl)); - } - catch (IllegalAccessException e) { - BCLib.LOGGER.error("Could not access " + fl); - } - } - } - return res; - } - - protected void onInit(){ - getAllOptions().forEach(e -> get(e.token)); - this.saveChanges(); - } - - /** - * The value without any check of {@link DependendConfigToken} - *

- * In most cases you probably want to use {@link #get(ConfigToken)}, we use this Method if we - * present the actual value of the Settings from the Config File without any additional processing. - * - * @param what The Option you want to get - * @param The Type of the Option - * @return The Value of the Option (without checking the {@link DependendConfigToken}): - */ - public T getRaw(ConfigToken what){ - return _get(what, true); - } - - /** - * The value of an Option - * @param what he Option you want to get - * @param The Type of the Option - * @return The Value of the Option. If this option is a {@link DependendConfigToken}, the returned value - * may not be the value from the config File. For Example, on a {@link Boolean}-Type the result is always false - * if {@link DependendConfigToken#dependenciesTrue} returns {@code false}. - */ - public T get(ConfigToken what){ - return _get(what, false); - } - - @SuppressWarnings("unchecked") - private T _get(ConfigToken what, boolean raw){ - //TODO: Check if we can make config fully Generic to avoid runtime type checks... - if (BooleanEntry.class.isAssignableFrom(what.type)){ - return (T)_getBoolean((ConfigToken)what, raw); - } - if (IntegerEntry.class.isAssignableFrom(what.type)){ - return (T)_getInt((ConfigToken)what); - } - if (FloatEntry.class.isAssignableFrom(what.type)){ - return (T)_getFloat((ConfigToken)what); - } - if (StringEntry.class.isAssignableFrom(what.type)){ - return (T)_getString((ConfigToken)what); - } - if (StringArrayEntry.class.isAssignableFrom(what.type)){ - return (T)_getStringArray((ConfigToken>)what); - } - return this._get(what); - } - - private T _get(ConfigToken what){ - BCLib.LOGGER.error(what + " has unsupported Type."); - return what.defaultValue; - } - - public void set(ConfigToken what, boolean value) { - this.setBoolean(what, value); - } - private Boolean _getBoolean(ConfigToken what, boolean raw){ - if (!raw && !what.dependenciesTrue(this)){ - return false; - } - - return this.getBoolean(what, what.defaultValue); - } - - public void set(ConfigToken what, int value) { - this.setInt(what, value); - } - private Integer _getInt(ConfigToken what){ - return this.getInt(what, what.defaultValue); - } - - public void set(ConfigToken what, float value) { - this.setFloat(what, value); - } - private Float _getFloat(ConfigToken what){ - return this.getFloat(what, what.defaultValue); - } - - public void set(ConfigToken what, String value) { - this.setString(what, value); - } - private String _getString(ConfigToken what){ - return this.getString(what, what.defaultValue); - } - - public void set(ConfigToken> what, List value) { - this.setStringArray(what, value); - } - - private List _getStringArray(ConfigToken> what){ - return this.getStringArray(what, what.defaultValue); - } - - -} diff --git a/src/main/java/ru/bclib/config/PathConfig.java b/src/main/java/ru/bclib/config/PathConfig.java deleted file mode 100644 index 297b986b..00000000 --- a/src/main/java/ru/bclib/config/PathConfig.java +++ /dev/null @@ -1,169 +0,0 @@ -package ru.bclib.config; - -import org.jetbrains.annotations.Nullable; -import ru.bclib.config.ConfigKeeper.Entry; -import ru.bclib.config.ConfigKeeper.FloatRange; -import ru.bclib.config.ConfigKeeper.IntegerRange; - -import java.util.List; - -public class PathConfig extends Config { - public PathConfig(String modID, String group, boolean autoSync, boolean diffContent) { - super(modID, group, autoSync, diffContent); - } - - public PathConfig(String modID, String group, boolean autoSync) { - super(modID, group, autoSync); - } - - public PathConfig(String modID, String group) { - super(modID, group); - } - - @Override - protected void registerEntries() {} - - protected static ConfigKey createKey(String category, String key) { - return new ConfigKey(key, category.split("\\.")); - } - - protected static ConfigKey createKey(String key) { - return createKey("", key); - } - - @Nullable - public > E getEntry(String category, String key, Class type) { - return this.getEntry(createKey(category, key), type); - } - - @Nullable - public > T getDefault(String category, String key, Class type) { - return this.getDefault(createKey(category, key), type); - } - - public String getString(String category, String key, String defaultValue) { - return this.getString(createKey(category, key), defaultValue); - } - - public String getString(String category, String key) { - return this.getString(createKey(category, key)); - } - - public boolean setString(String category, String key, String value) { - return this.setString(createKey(category, key), value); - } - - public int getInt(String category, String key, int defaultValue) { - return this.getInt(createKey(category, key), defaultValue); - } - - public int getInt(String category, String key) { - return this.getInt(createKey(category, key)); - } - - public boolean setInt(String category, String key, int value) { - return this.setInt(createKey(category, key), value); - } - - public boolean setRangedInt(String category, String key, int value) { - return this.setRanged(createKey(category, key), value, IntegerRange.class); - } - - public boolean setRangedFloat(String category, String key, float value) { - return this.setRanged(createKey(category, key), value, FloatRange.class); - } - - public float getFloat(String category, String key, float defaultValue) { - return this.getFloat(createKey(category, key), defaultValue); - } - - public float getFloat(String category, String key) { - return this.getFloat(createKey(category, key)); - } - - public boolean setFloat(String category, String key, float value) { - return this.setFloat(createKey(category, key), value); - } - - public boolean getBoolean(String category, String key, boolean defaultValue) { - return this.getBoolean(createKey(category, key), defaultValue); - } - - public boolean getBoolean(String category, String key) { - return this.getBoolean(createKey(category, key)); - } - - public boolean setBoolean(String category, String key, boolean value) { - return this.setBoolean(createKey(category, key), value); - } - - public List getStringArray(String category, String key, List defaultValue) { - return this.getStringArray(createKey(category, key), defaultValue); - } - - public List getStringArray(String category, String key) { - return this.getStringArray(createKey(category, key)); - } - - public boolean setStringArray(String category, String key, List value) { - return this.setStringArray(createKey(category, key), value); - } - - // From Root - - public String getStringRoot(String key, String defaultValue) { - return this.getString(createKey(key), defaultValue); - } - - public String getStringRoot(String key) { - return this.getString(createKey(key)); - } - - public boolean setStringRoot(String key, String value) { - return this.setString(createKey(key), value); - } - - public int getIntRoot(String key, int defaultValue) { - return this.getInt(createKey(key), defaultValue); - } - - public int getIntRoot(String key) { - return this.getInt(createKey(key)); - } - - public boolean setIntRoot(String key, int value) { - return this.setInt(createKey(key), value); - } - - public boolean setRangedIntRoot(String key, int value) { - return this.setRanged(createKey(key), value, IntegerRange.class); - } - - public boolean setRangedFloatRoot(String key, float value) { - return this.setRanged(createKey(key), value, FloatRange.class); - } - - public float getFloatRoot(String key, float defaultValue) { - return this.getFloat(createKey(key), defaultValue); - } - - public float getFloatRoot(String key) { - return this.getFloat(createKey(key)); - } - - public boolean setFloatRoot(String key, float value) { - return this.setFloat(createKey(key), value); - } - - public boolean getBooleanRoot(String key, boolean defaultValue) { - return this.getBoolean(createKey(key), defaultValue); - } - - public boolean getBooleanRoot(String key) { - return this.getBoolean(createKey(key)); - } - - public boolean setBooleanRoot(String key, boolean value) { - return this.setBoolean(createKey(key), value); - } -} diff --git a/src/main/java/ru/bclib/config/ServerConfig.java b/src/main/java/ru/bclib/config/ServerConfig.java deleted file mode 100644 index 811a71a9..00000000 --- a/src/main/java/ru/bclib/config/ServerConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -package ru.bclib.config; - -import ru.bclib.BCLib; -import ru.bclib.api.dataexchange.handler.autosync.AutoSync; - -import java.util.ArrayList; -import java.util.List; - -public class ServerConfig extends NamedPathConfig { - public static final ConfigToken ENABLED = ConfigToken.Boolean(true, "enabled", AutoSync.SYNC_CATEGORY); - public static final DependendConfigToken OFFER_CONFIGS = DependendConfigToken.Boolean(true, "offerConfigs", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - public static final DependendConfigToken OFFER_FILES = DependendConfigToken.Boolean(true, "offerFiles", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - public static final DependendConfigToken OFFER_MODS = DependendConfigToken.Boolean(true, "offerMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - public static final DependendConfigToken OFFER_ALL_MODS = DependendConfigToken.Boolean(false, "offerAllMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(OFFER_MODS)); - public static final DependendConfigToken SEND_ALL_MOD_INFO = DependendConfigToken.Boolean(false, "sendAllModInfo", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED)); - - - public static final ConfigToken> ADDITIONAL_MODS = ConfigToken.StringArray(new ArrayList<>(0), "additionalMods", AutoSync.SYNC_CATEGORY); - public static final ConfigToken> EXCLUDED_MODS = ConfigToken.StringArray(new ArrayList<>(0), "excludeMods", AutoSync.SYNC_CATEGORY); - - - public ServerConfig() { - super(BCLib.MOD_ID, "server", false); - } - - public boolean isAllowingAutoSync() { - return get(ENABLED); - } - - public boolean isOfferingConfigs() { - return get(OFFER_CONFIGS) /*&& isAllowingAutoSync()*/; - } - - public boolean isOfferingFiles() { - return get(OFFER_FILES) /*&& isAllowingAutoSync()*/; - } - - public boolean isOfferingMods() { - return get(OFFER_MODS) /*&& isAllowingAutoSync()*/; - } - - public boolean isOfferingAllMods() { - return get(OFFER_ALL_MODS) /*&& isAllowingAutoSync()*/; - } - - public boolean isOfferingInfosForMods() { - return get(SEND_ALL_MOD_INFO) /*&& isAllowingAutoSync()*/; - } - -} diff --git a/src/main/java/ru/bclib/entity/DespawnableAnimal.java b/src/main/java/ru/bclib/entity/DespawnableAnimal.java deleted file mode 100644 index 197673a1..00000000 --- a/src/main/java/ru/bclib/entity/DespawnableAnimal.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.bclib.entity; - -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.animal.Animal; -import net.minecraft.world.level.Level; - -public abstract class DespawnableAnimal extends Animal { - protected DespawnableAnimal(EntityType entityType, Level level) { - super(entityType, level); - } - - @Override - public boolean removeWhenFarAway(double d) { - return !this.hasCustomName(); - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridCell.java deleted file mode 100644 index 6e99bbea..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridCell.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import ru.bclib.interfaces.TriConsumer; - -import java.util.List; -import java.util.function.Function; - -@Environment(EnvType.CLIENT) -class GridCell extends GridCellDefinition { - public final float height; - Function componentPlacer; - TriConsumer customRender; - - GridCell(double width, double height, GridLayout.GridValueType widthType, Function componentPlacer, TriConsumer customRender) { - super(width, widthType); - this.height = (float) height; - this.componentPlacer = componentPlacer; - this.customRender = customRender; - } - - protected GridElement buildElementAt(int left, int top, int width, final List collector) { - return new GridElement(left, top, width, (int) this.height, componentPlacer, customRender); - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java deleted file mode 100644 index dc6d3c23..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java +++ /dev/null @@ -1,77 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.gui.components.Checkbox; -import net.minecraft.network.chat.Component; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; - -import java.util.function.Consumer; - -@Environment(EnvType.CLIENT) -class SignalingCheckBox extends Checkbox{ - private Consumer onChange; - public SignalingCheckBox(int left, int top, int width, int height, Component component, boolean checked, Consumer onChange) { - super(left, top, width, height, component, checked); - this.onChange = onChange; - if (onChange!=null) - onChange.accept(checked); - } - - @Override - public void onPress() { - super.onPress(); - if (onChange!=null) - onChange.accept(this.selected()); - } -} - -@Environment(EnvType.CLIENT) -public class GridCheckboxCell extends GridCell implements GridWidgetWithEnabledState{ - private boolean checked; - private Checkbox lastCheckbox; - private boolean enabled; - private final float alpha; - - GridCheckboxCell(Component text, boolean checked, float alpha, double width, GridValueType widthType, double height) { - this(text, checked, alpha, width, widthType, height, null); - } - - GridCheckboxCell(Component text, boolean checked, float alpha, double width, GridValueType widthType, double height, Consumer onChange) { - super(width, height, widthType, null, null); - lastCheckbox = null; - enabled = true; - this.alpha = alpha; - this.componentPlacer = (transform) -> { - Checkbox cb = new SignalingCheckBox(transform.left, transform.top, transform.width, transform.height, - text, - checked, - (state)-> { - this.checked = state; - if (onChange!=null) onChange.accept(state); - } - ); - cb.setAlpha(alpha); - lastCheckbox = cb; - setEnabled(enabled); - return cb; - }; - - } - - public boolean isChecked(){ - return checked; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - if (lastCheckbox!=null){ - lastCheckbox.active = enabled; - lastCheckbox.setAlpha(enabled?alpha:(alpha *0.5f)); - } - this.enabled = enabled; - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridColumn.java b/src/main/java/ru/bclib/gui/gridlayout/GridColumn.java deleted file mode 100644 index d363061b..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridColumn.java +++ /dev/null @@ -1,72 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; -import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment; - -import java.util.List; - -@Environment(EnvType.CLIENT) -public class GridColumn extends GridContainer { - GridColumn(double width) { - super(width); - } - - GridColumn(double width, GridLayout.GridValueType widthType) { - super(width, widthType); - } - - public GridRow addRow() { - return addRow(VerticalAlignment.TOP); - } - - public GridRow addRow(VerticalAlignment align) { - GridRow row = new GridRow(1.0, widthType==GridValueType.INHERIT?GridValueType.INHERIT:GridLayout.GridValueType.PERCENTAGE, align); - this.cells.add(row); - return row; - } - - - public void addSpacerRow() { - this.addSpacerRow(4); - } - - public void addSpacerRow(int height) { - GridCell cell = new GridCell(1.0, height, GridValueType.PERCENTAGE, null, null); - this.cells.add(cell); - } - - @Override - public int calculateWidth(final int parentWidth){ - if (widthType == GridValueType.INHERIT) { - return cells.stream() - .filter(row->row.widthType == GridValueType.INHERIT) - .map(row -> row.buildElement(0, 0, 1, 0, 0, null).width) - .reduce(0, (p, c) -> Math.max(p, c)); - - } else { - return super.calculateWidth(parentWidth); - } - } - - - - @Override - protected GridElement buildElementAt(int left, int inTop, int width, final List collector) { - int height = 0; - int top = inTop; - - if (widthType == GridValueType.INHERIT) { - width = calculateWidth(width); - } - - for (GridCellDefinition row : cells) { - GridElement element = row.buildElement(width, 0, 1, left, top, collector); - top += element.height; - height += element.height; - } - - return new GridElement(left, inTop, width, height); - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridCustomRenderCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridCustomRenderCell.java deleted file mode 100644 index d79229ea..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridCustomRenderCell.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; - - -@Environment(EnvType.CLIENT) -public abstract class GridCustomRenderCell extends GridCell{ - protected GridCustomRenderCell(double width, GridValueType widthType, double height) { - super(width, height, widthType, null, null); - this.customRender = this::onRender; - } - - public abstract void onRender(PoseStack poseStack, GridTransform transform, Object context); -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridImageCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridImageCell.java deleted file mode 100644 index 51db3049..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridImageCell.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.gui.GuiComponent; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; - -@Environment(EnvType.CLIENT) -public class GridImageCell extends GridCell{ - GridImageCell(ResourceLocation location, double width, GridValueType widthType, double height, float alpha, int uvLeft, int uvTop, int uvWidth, int uvHeight, int resourceWidth, int resourceHeight) { - super(width, height, widthType, null, (poseStack, transform, context) -> { - RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderTexture(0, location); - RenderSystem.enableBlend(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, alpha); - GuiComponent.blit(poseStack, transform.left, transform.top, transform.width, transform.height, uvLeft, uvTop, uvWidth, uvHeight, resourceWidth, resourceHeight); - }); - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridLayout.java b/src/main/java/ru/bclib/gui/gridlayout/GridLayout.java deleted file mode 100644 index 5e9ef459..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridLayout.java +++ /dev/null @@ -1,204 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import com.mojang.blaze3d.vertex.PoseStack; -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 java.util.LinkedList; -import java.util.List; -import java.util.function.Function; - - -@Environment(EnvType.CLIENT) -abstract class GridCellDefinition { - public final float width; - public final GridLayout.GridValueType widthType; - - public GridCellDefinition(double width, GridLayout.GridValueType widthType) { - this.width = (float)width; - this.widthType = widthType; - } - - public - int calculateWidth(final int parentWidth){ - if (widthType == GridLayout.GridValueType.CONSTANT) { - return (int) this.width; - } else if (widthType == GridValueType.PERCENTAGE) { - return (int) (this.width * parentWidth); - } else { - return 0; - } - } - - final GridElement buildElement(final int parentWidth, final int autoWidth, final float autoWidthSum, int left, final int top, final List collector) { - final int width = widthType == GridValueType.FILL ?(int)((this.width/autoWidthSum)*autoWidth):calculateWidth(parentWidth); - - final GridElement el = buildElementAt(left, top, width, collector); - if (collector!=null) { - collector.add(el); - } - return el; - } - - abstract protected GridElement buildElementAt(int left, int top, int width, final List collector); -} - -@Environment(EnvType.CLIENT) -class GridElement extends GridTransform{ - final Function componentPlacer; - final TriConsumer customRender; - Object renderContext; - - GridElement(int left, int top, int width, int height, Function componentPlacer, TriConsumer customRender) { - super(left, top, width, height); - this.componentPlacer = componentPlacer; - this.customRender = customRender; - } - - GridElement(int left, int top, int width, int height) { - this(left, top, width, height, null, null); - } - - GridTransform transformWithPadding(int leftPadding, int topPadding){ - return new GridTransform(left + leftPadding, top + topPadding, width, height); - } -} - -@Environment(EnvType.CLIENT) -abstract class GridContainer extends GridCellDefinition{ - protected List cells; - - public GridContainer(double width) { - this(width, GridLayout.GridValueType.CONSTANT); - } - - GridContainer(double width, GridLayout.GridValueType widthType) { - super(width, widthType); - cells = new LinkedList<>(); - } -} - -@Environment(EnvType.CLIENT) -public class GridLayout extends GridColumn { - public static final int COLOR_WHITE = 0xFFFFFFFF; - 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; - - public final GridScreen screen; - public final int screenHeight; - public final int sidePadding; - public final int initialTopPadding; - public final boolean centerVertically; - private int height; - private int topPadding; - - private List elements; - - public GridLayout(GridScreen screen) { - this(screen, 0, true); - } - - public GridLayout(GridScreen screen, int topPadding, boolean centerVertically) { - this(screen, topPadding, 20, centerVertically); - } - - public GridLayout(GridScreen screen, int topPadding, int sidePadding, boolean centerVertically) { - super(screen.width-2*sidePadding, GridValueType.CONSTANT); - this.screen = screen; - this.screenHeight = screen.height; - height = 0; - this.topPadding = topPadding; - this.sidePadding = sidePadding; - this.initialTopPadding = topPadding; - this.centerVertically = centerVertically; - } - - public int getHeight(){ - return height; - } - - public int getTopPadding() { - return topPadding; - } - - void buildLayout(){ - elements = new LinkedList<>(); - GridElement el = this.buildElement((int)this.width, 0, 1, 0,0, elements); - this.height = el.height; - if (centerVertically && el.height + initialTopPadding < screenHeight) { - topPadding = (screenHeight - el.height) >> 1; - } else { - topPadding = initialTopPadding; - } - - } - - public List> movableWidgets = new LinkedList<>(); - public void finalizeLayout(){ - buildLayout(); - - elements - .stream() - .filter(element -> element.componentPlacer!=null) - .forEach(element -> { - final GridTransform transform = element.transformWithPadding(sidePadding, topPadding); - final Object context = element.componentPlacer.apply(transform); - if (element.customRender != null){ - element.renderContext = context; - } else if (context instanceof AbstractWidget) { - final AbstractWidget widget = (AbstractWidget)context; - movableWidgets.add(new Pair(widget, widget.y)); - screen.addRenderableWidget(widget); - } - }); - } - - public void render(PoseStack poseStack){ - if (elements == null) return; - elements - .stream() - .filter(element -> element.customRender!=null) - .forEach(element -> element.customRender.accept(poseStack, element.transformWithPadding(sidePadding, topPadding), element.renderContext)); - } - - - public static enum VerticalAlignment { - TOP, CENTER, BOTTOM - } - - public static enum Alignment { - LEFT, CENTER, RIGHT - } - - /** - * Determines how a measurement value is interpreted - */ - public static enum GridValueType { - /** - * The value is a constant pixel size - */ - CONSTANT, - /** - * The Value is relative to the parent size - */ - PERCENTAGE, - /** - * The value will be set to fill up the remaining space (i.e. when this is applied to a width of a row element, - * a {@link #FILL}-type may be used to right align (FILL - CONSTANT) or center (FILL - CONSTANT - FILL) elements. - */ - FILL, - /** - * Calculate size based on child-elements - */ - INHERIT; - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridMessageCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridMessageCell.java deleted file mode 100644 index 8c3c053c..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridMessageCell.java +++ /dev/null @@ -1,63 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.components.MultiLineLabel; -import net.minecraft.network.chat.Component; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; - -import java.util.List; - -@Environment(EnvType.CLIENT) -public class GridMessageCell extends GridCell { - private final Font font; - private Component text; - private MultiLineLabel lastLabel; - private GridTransform lastTransform; - - GridMessageCell(double width, GridValueType widthType, Alignment contentAlignment, Font font, Component text) { - this(width, widthType, contentAlignment, font, text, GridLayout.COLOR_WHITE); - } - GridMessageCell(double width, GridValueType widthType, Alignment contentAlignment, Font font, Component text, int color) { - super(width, -1, widthType, null, null); - this.font = font; - this.text = text; - - customRender = (poseStack, transform, context) -> { - //MultiLineLabel label = (MultiLineLabel) context; - if (contentAlignment == Alignment.CENTER) { - lastLabel.renderCentered(poseStack, transform.width / 2 + transform.left, transform.top, font.lineHeight, color); - } - else if (contentAlignment == Alignment.LEFT) { - lastLabel.renderLeftAligned(poseStack, transform.left, transform.top, font.lineHeight, color); - } - }; - } - - public void setText(Component text){ - this.text = text; - if (lastTransform!=null) { - create(lastTransform); - } - } - - private MultiLineLabel getLabel(GridTransform transform) { - return lastLabel; - } - - protected void create(GridTransform transform) { - this.lastTransform = transform; - this.lastLabel = MultiLineLabel.create(font, text, transform.width); - } - - @Override - protected GridElement buildElementAt(int left, int top, int width, List collector) { - create(new GridTransform(left, top, width, 0)); - int promptLines = this.lastLabel.getLineCount() + 1; - int height = promptLines * 9; - - return new GridElement(left, top, width, height, this::getLabel, customRender); - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridRow.java b/src/main/java/ru/bclib/gui/gridlayout/GridRow.java deleted file mode 100644 index 650a49d2..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridRow.java +++ /dev/null @@ -1,275 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.Button.OnPress; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; -import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -@Environment(EnvType.CLIENT) -public class GridRow extends GridContainer { - public final GridLayout.VerticalAlignment alignment; - - GridRow(double width) { - this(width, VerticalAlignment.TOP); - } - - GridRow(double width, GridLayout.GridValueType widthType) { - this(width, widthType, VerticalAlignment.CENTER); - } - - GridRow(double width, GridLayout.VerticalAlignment alignment) { - super(width); - this.alignment = alignment; - } - - GridRow(double width, GridLayout.GridValueType widthType, GridLayout.VerticalAlignment alignment) { - super(width, widthType); - this.alignment = alignment; - } - - public GridColumn addColumn(double width, GridLayout.GridValueType widthType) { - GridColumn cell = new GridColumn(width, widthType); - this.cells.add(cell); - return cell; - } - - public GridCell addComponent(double height, Function componentPlacer) { - return addComponent(1.0, GridLayout.GridValueType.PERCENTAGE, height, componentPlacer); - } - - public GridCell addComponent(double width, GridLayout.GridValueType widthType, double height, Function componentPlacer) { - GridCell cell = new GridCell(width, height, widthType, componentPlacer, null); - this.cells.add(cell); - return cell; - } - - - public GridCell addButton(Component text, double height, OnPress onPress) { - return addButton(text, 1.0, GridValueType.PERCENTAGE, height, onPress); - } - - public GridCell addButton(Component text, float alpha, double height, OnPress onPress) { - return addButton(text, alpha, 1.0, GridValueType.PERCENTAGE, height, onPress); - } - - public GridCell addButton(Component text, double height, Font font, OnPress onPress) { - return addButton(text, 1.0f, height, font, onPress); - } - - public GridCell addButton(Component text, float alpha, double height, Font font, OnPress onPress) { - final int width = font.width(text.getVisualOrderText()) + 24; - return addButton(text, alpha, width, GridValueType.CONSTANT, height, onPress); - } - - public GridCell addButton(Component text, double width, GridValueType widthType, double height, OnPress onPress) { - return addButton(text, 1.0f, width, widthType, height, onPress); - } - - public GridCell addButton(Component text, float alpha, double width, GridValueType widthType, double height, OnPress onPress) { - GridCell cell = new GridCell(width, height, widthType, (transform) -> { - Button customButton = new Button(transform.left, transform.top, transform.width, transform.height, text, onPress); - customButton.setAlpha(alpha); - return customButton; - }, null); - this.cells.add(cell); - return cell; - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, Font font, Consumer onChange){ - final int width = font.width(text.getVisualOrderText()) + 24 + 2 * 12; - - GridCheckboxCell cell = new GridCheckboxCell(text, checked, 1.0f, width, widthType, 20, onChange); - this.cells.add(cell); - return cell; - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, int height) { - return addCheckbox(text, checked, 1.0f, height); - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, int height) { - return addCheckbox(text, checked, alpha, 1.0, GridValueType.PERCENTAGE, height); - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, int height, Font font) { - return addCheckbox(text, checked, 1.0f, height, font); - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, int height, Font font) { - final int width = font.width(text.getVisualOrderText()) + 24 + 2 * 12; - return addCheckbox(text, checked, alpha, width, GridValueType.CONSTANT, height); - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, double width, GridValueType widthType, int height) { - return addCheckbox(text, checked, 1.0f, width, widthType, height); - } - - public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, double width, GridValueType widthType, int height) { - GridCheckboxCell cell = new GridCheckboxCell(text, checked, alpha, width, widthType, height); - this.cells.add(cell); - return cell; - } - - public GridCustomRenderCell addCustomRender(GridCustomRenderCell cell) { - this.cells.add(cell); - return cell; - } - - public GridCell addImage(ResourceLocation location, int width, int height) { - return addImage(location, 1.0f, width, height); - } - - public GridCell addImage(ResourceLocation location, float alpha, int width, int height) { - return addImage(location, alpha, width, GridValueType.CONSTANT, height, 0, 0, width, height, width, height); - } - - public GridCell addImage(ResourceLocation location, double width, GridValueType widthType, int height, int resourceWidth, int resourceHeight) { - return addImage(location, 1.0f, width, widthType, height, resourceWidth, resourceHeight); - } - - public GridCell addImage(ResourceLocation location, float alpha, double width, GridValueType widthType, int height, int resourceWidth, int resourceHeight) { - return addImage(location, alpha, width, widthType, height, 0, 0, resourceWidth, resourceWidth, resourceWidth, resourceHeight); - } - - public GridCell addImage(ResourceLocation location, double width, GridValueType widthType, int height, int uvLeft, int uvTop, int uvWidth, int uvHeight, int resourceWidth, int resourceHeight) { - return addImage(location, 1.0f, width, widthType, height, uvLeft, uvTop, uvWidth, uvHeight, resourceWidth, resourceHeight); - } - - public GridCell addImage(ResourceLocation location, float alpha, double width, GridValueType widthType, int height, int uvLeft, int uvTop, int uvWidth, int uvHeight, int resourceWidth, int resourceHeight) { - GridCell cell = new GridImageCell(location, width, widthType, height, alpha, uvLeft, uvTop, uvWidth, uvHeight, resourceWidth, resourceHeight); - this.cells.add(cell); - return cell; - } - - - public GridColumn addFiller() { - return addFiller(1); - } - - public GridColumn addFiller(float portion) { - GridColumn cell = new GridColumn(portion, GridValueType.FILL); - this.cells.add(cell); - return cell; - } - - public void addSpacer() { - addSpacer(12); - } - - public void addSpacer(int width) { - GridCell cell = new GridCell(width, 0, GridValueType.CONSTANT, null, null); - this.cells.add(cell); - } - - - public GridMessageCell addMessage(Component text, Font font, Alignment contentAlignment) { - return addMessage(text, font, GridLayout.COLOR_WHITE, contentAlignment); - } - - public GridMessageCell addMessage(Component text, Font font, int color, Alignment contentAlignment) { - return addMessage(text, 1.0, GridLayout.GridValueType.PERCENTAGE, font, color, contentAlignment); - } - - public GridMessageCell addMessage(Component text, double width, GridValueType widthType, Font font, Alignment contentAlignment) { - return addMessage(text, width, widthType, font, GridLayout.COLOR_WHITE, contentAlignment); - } - - public GridMessageCell addMessage(Component text, double width, GridValueType widthType, Font font, int color, Alignment contentAlignment) { - GridMessageCell cell = new GridMessageCell(width, widthType, contentAlignment, font, text, color); - this.cells.add(cell); - return cell; - } - - public GridStringCell addString(Component text, GridScreen parent) { - return this.addString(text, GridLayout.COLOR_WHITE, parent); - } - - - public GridStringCell addString(Component text, int color, GridScreen parent) { - final int width = parent.getWidth(text); - return this.addString(text, width, GridValueType.CONSTANT, color, Alignment.CENTER, parent); - } - - public GridStringCell addString(Component text, Alignment contentAlignment, GridScreen parent) { - return this.addString(text, GridLayout.COLOR_WHITE, contentAlignment, parent); - } - - public GridStringCell addString(Component text, int color, Alignment contentAlignment, GridScreen parent) { - return this.addString(text, 1.0, GridLayout.GridValueType.PERCENTAGE, color, contentAlignment, parent); - } - - public GridStringCell addString(Component text, double width, GridValueType widthType, Alignment contentAlignment, GridScreen parent) { - return addString(text, width, widthType, GridLayout.COLOR_WHITE, contentAlignment, parent); - } - - public GridStringCell addString(Component text, double width, GridValueType widthType, int color, Alignment contentAlignment, GridScreen parent) { - GridStringCell cell = new GridStringCell(width, widthType, parent.getFont().lineHeight, contentAlignment, parent, text, color); - this.cells.add(cell); - return cell; - } - - @Override - protected GridElement buildElementAt(int inLeft, int top, int width, final List collector) { - int height = 0; - int left = inLeft; - if (widthType == GridValueType.INHERIT) { - final int originalWidth = width; - width = cells.stream() - .filter(row -> row.widthType == GridValueType.CONSTANT || row.widthType == GridValueType.INHERIT) - .map(row -> row.buildElement(0, 0, 1, 0, 0, null).width) - .reduce(0, (p, c) -> p+c); - } - - final int inheritedWidth = width; - final int fixedWidth = cells.stream() - .filter(col -> col.widthType != GridValueType.FILL) - .map(col -> col.calculateWidth(inheritedWidth)) - .reduce(0, (p, c) -> p + c); - final float autoWidthSum = cells.stream() - .filter(col -> col.widthType == GridValueType.FILL) - .map(col -> col.width) - .reduce(0.0f, (p, c) -> p + c); - final int autoWidth = width - fixedWidth; - - if (alignment == VerticalAlignment.TOP) { - for (GridCellDefinition col : cells) { - GridElement element = col.buildElement(width, autoWidth, autoWidthSum, left, top, collector); - left += element.width; - height = Math.max(height, element.height); - } - } - else { - //first iteration will collect heights, second one will transform top position for alignment - Map cache = new HashMap<>(); - for (GridCellDefinition col : cells) { - GridElement element = col.buildElement(width, autoWidth, autoWidthSum, left, top, null); - left += element.width; - height = Math.max(height, element.height); - cache.put(col, element); - } - - left = inLeft; - for (GridCellDefinition col : cells) { - GridElement element = cache.get(col); - final int topOffset = (alignment == VerticalAlignment.BOTTOM) ? (height - element.height) : (height - element.height) >> 1; - element = col.buildElement(width, autoWidth, autoWidthSum, left, top + topOffset, collector); - left += element.width; - } - } - - - return new GridElement(inLeft, top, width, height); - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridScreen.java b/src/main/java/ru/bclib/gui/gridlayout/GridScreen.java deleted file mode 100644 index 663ca0dd..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridScreen.java +++ /dev/null @@ -1,257 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.Tesselator; -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.Widget; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarratableEntry; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.network.chat.Component; -import net.minecraft.util.Mth; -import org.jetbrains.annotations.Nullable; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; - - -@Environment(EnvType.CLIENT) -public abstract class GridScreen extends Screen { - protected GridLayout grid = null; - public final int topPadding; - public final int sidePadding; - public final boolean centerVertically; - @Nullable - public final Screen parent; - - protected int scrollPos = 0; - - public GridScreen(Component title) { - this(null, title); - } - - public GridScreen(@Nullable Screen parent, Component title){ - this(parent, title, 0, true); - } - - public GridScreen(Component title, int topPadding, boolean centerVertically) { - this(null, title, topPadding, 20, centerVertically); - } - - public GridScreen(@Nullable Screen parent, Component title, int topPadding, boolean centerVertically) { - this(parent, title, topPadding, 20, centerVertically); - } - - public GridScreen(Component title, int topPadding, int sidePadding, boolean centerVertically) { - this(null, title, topPadding, sidePadding, centerVertically); - } - - public GridScreen(@Nullable Screen parent, Component title, int topPadding, int sidePadding, boolean centerVertically) { - super(title); - - this.parent = parent; - this.topPadding = topPadding; - this.sidePadding = sidePadding; - this.centerVertically = centerVertically; - } - - @Override - public void onClose() { - this.minecraft.setScreen(parent); - } - - public Font getFont(){ - return this.font; - } - - @Override - public boolean isPauseScreen() { - return true; - } - - @Override - public T addRenderableWidget(T guiEventListener) { - return super.addRenderableWidget(guiEventListener); - } - - protected void addTitle(){ - grid.addRow().addString(this.title, Alignment.CENTER, this); - grid.addSpacerRow(15); - } - - final protected void init() { - super.init(); - this.grid = new GridLayout(this, this.topPadding, this.sidePadding, this.centerVertically); - - addTitle(); - - initLayout(); - grid.finalizeLayout(); - } - - protected abstract void initLayout(); - - protected void renderScreen(PoseStack poseStack, int i, int j, float f) { - super.render(poseStack, i, j, f); - } - - public void render(PoseStack poseStack, int i, int j, float f) { - this.renderDirtBackground(i); - renderGrid(poseStack); - super.render(poseStack, i, j, f); - } - - protected void renderGrid(PoseStack poseStack) { - if (grid!=null) { - if (isScrollable()) { - for (var item : grid.movableWidgets) { - item.first.y = item.second + scrollPos; - } - - renderScroll(poseStack); - - poseStack.pushPose(); - poseStack.translate(0, scrollPos, 0); - grid.render(poseStack); - poseStack.popPose(); - } else { - grid.render(poseStack); - } - } - } - - public static int getWidth(Component text, Font font) { - return font.width(text.getVisualOrderText()); - } - - public int getWidth(Component text) { - return getWidth(text, getFont()); - } - - public void setScrollPos(int sp) { - scrollPos = Math.max(getMaxScrollPos(), Math.min(0, sp)); - } - - public int getScrollPos() { - return scrollPos; - } - - public int getScrollHeight() { - if (grid!=null) return grid.getHeight() + topPadding; - return height; - } - - public int getMaxScrollPos() { - return height - (getScrollHeight() + topPadding); - } - - public boolean isScrollable() { - return height= 0 && y <= height && x >= width-SCROLLER_WIDTH && x <= width; - } - - private boolean scrolling = false; - protected void updateScrollingState(double x, double y, int i) { - this.scrolling = i == 0 && x >= width-SCROLLER_WIDTH && x < width; - } - - private static final int SCROLLER_WIDTH = 6; - private void renderScroll(PoseStack poseStack){ - final int y1 = height; - final int y0 = 0; - final int yd = y1 - y0; - final int maxPosition = getScrollHeight() + topPadding; - - final int x0 = width-SCROLLER_WIDTH; - final int x1 = width; - - Tesselator tesselator = Tesselator.getInstance(); - BufferBuilder bufferBuilder = tesselator.getBuilder(); - RenderSystem.disableTexture(); - RenderSystem.setShader(GameRenderer::getPositionColorShader); - int widgetHeight = (int)((float)(yd*yd) / (float)maxPosition); - widgetHeight = Mth.clamp(widgetHeight, 32, yd - 8); - float relPos = (float)this.getScrollPos() / this.getMaxScrollPos(); - int top = (int)(relPos * (yd - widgetHeight)) + y0; - if (top < y0) { - top = y0; - } - - bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - - //scroller background - bufferBuilder.vertex((double)x0, (double)y1, 0.0D).color(0, 0, 0, 255).endVertex(); - bufferBuilder.vertex((double)x1, (double)y1, 0.0D).color(0, 0, 0, 255).endVertex(); - bufferBuilder.vertex((double)x1, (double)y0, 0.0D).color(0, 0, 0, 255).endVertex(); - bufferBuilder.vertex((double)x0, (double)y0, 0.0D).color(0, 0, 0, 255).endVertex(); - - //scroll widget shadow - bufferBuilder.vertex((double)x0, (double)(top + widgetHeight), 0.0D).color(128, 128, 128, 255).endVertex(); - bufferBuilder.vertex((double)x1, (double)(top + widgetHeight), 0.0D).color(128, 128, 128, 255).endVertex(); - bufferBuilder.vertex((double)x1, (double)top, 0.0D).color(128, 128, 128, 255).endVertex(); - bufferBuilder.vertex((double)x0, (double)top, 0.0D).color(128, 128, 128, 255).endVertex(); - - //scroll widget - bufferBuilder.vertex((double)x0, (double)(top + widgetHeight - 1), 0.0D).color(192, 192, 192, 255).endVertex(); - bufferBuilder.vertex((double)(x1 - 1), (double)(top + widgetHeight - 1), 0.0D).color(192, 192, 192, 255).endVertex(); - bufferBuilder.vertex((double)(x1 - 1), (double)top, 0.0D).color(192, 192, 192, 255).endVertex(); - bufferBuilder.vertex((double)x0, (double)top, 0.0D).color(192, 192, 192, 255).endVertex(); - tesselator.end(); - } - - public boolean mouseClicked(double x, double y, int i) { - this.updateScrollingState(x, y, i); - if (this.scrolling) { - return true; - } else { - return super.mouseClicked(x, y, i); - } - } - - public boolean mouseDragged(double xAbs, double yAbs, int i, double dX, double dY) { - if (super.mouseDragged(xAbs, yAbs, i, dX, dY)) { - return true; - } else if (i == 0 && this.scrolling) { - if (yAbs < 0) { - this.setScrollPos(0); - } else if (yAbs > height) { - this.setScrollPos(this.getMaxScrollPos()); - } else { - this.setScrollPos((int)(this.getScrollPos() - dY * 2)); - } - - return true; - } else { - return false; - } - } - - public boolean mouseScrolled(double d, double e, double f) { - if (isScrollable()) { - setScrollPos((int) (scrollPos + f * 10)); - } - return true; - } - - public boolean keyPressed(int keyCode, int j, int k) { - if (super.keyPressed(keyCode, j, k)) { - return true; - } else if (keyCode == 264) { - this.mouseScrolled(0, -1.0f, 0); - return true; - } else if (keyCode == 265) { - this.mouseScrolled(0, 1.0, 0); - return true; - } else { - return false; - } - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridStringCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridStringCell.java deleted file mode 100644 index 3d4a4fe6..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridStringCell.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.network.chat.Component; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; - -@Environment(EnvType.CLIENT) -public class GridStringCell extends GridCell { - private Component text; - GridStringCell(double width, GridValueType widthType, int height, Alignment contentAlignment, GridScreen parent, Component text) { - this(width, widthType, height, contentAlignment, parent, text, GridLayout.COLOR_WHITE); - - } - GridStringCell(double width, GridValueType widthType, int height, Alignment contentAlignment, GridScreen parent, Component text, int color) { - super(width, height, widthType, null, null); - this.text = text; - this.customRender = (poseStack, transform, context) -> { - if (contentAlignment == Alignment.CENTER) { - parent.drawCenteredString(poseStack, parent.getFont(), this.text, transform.width / 2 + transform.left, transform.top, color); - } - else if (contentAlignment == Alignment.LEFT) { - parent.drawString(poseStack, parent.getFont(), this.text, transform.left, transform.top, color); - } - }; - } - - public void setText(Component newText){ - this.text = newText; - } -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridTransform.java b/src/main/java/ru/bclib/gui/gridlayout/GridTransform.java deleted file mode 100644 index 4d392b41..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridTransform.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.gui.gridlayout; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - -@Environment(EnvType.CLIENT) -public class GridTransform { - public final int left; - public final int top; - public final int width; - public final int height; - - GridTransform(int left, int top, int width, int height) { - this.left = left; - this.top = top; - this.width = width; - this.height = height; - } - - @Override - public String toString() { - return "{" + "left=" + left + ", top=" + top + ", width=" + width + ", height=" + height + '}'; - } - -} diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridWidgetWithEnabledState.java b/src/main/java/ru/bclib/gui/gridlayout/GridWidgetWithEnabledState.java deleted file mode 100644 index caa9f150..00000000 --- a/src/main/java/ru/bclib/gui/gridlayout/GridWidgetWithEnabledState.java +++ /dev/null @@ -1,6 +0,0 @@ -package ru.bclib.gui.gridlayout; - -public interface GridWidgetWithEnabledState { - public boolean isEnabled(); - public void setEnabled(boolean enabled); -} diff --git a/src/main/java/ru/bclib/gui/modmenu/EntryPoint.java b/src/main/java/ru/bclib/gui/modmenu/EntryPoint.java deleted file mode 100644 index 56590210..00000000 --- a/src/main/java/ru/bclib/gui/modmenu/EntryPoint.java +++ /dev/null @@ -1,12 +0,0 @@ -package ru.bclib.gui.modmenu; - -import ru.bclib.integration.modmenu.ModMenuIntegration; - -@Deprecated() -public class EntryPoint extends ModMenuIntegration { - public static final Object entrypointObject = createEntrypoint(new EntryPoint()); - - public EntryPoint() { - super(MainScreen::new); - } -} diff --git a/src/main/java/ru/bclib/gui/modmenu/MainScreen.java b/src/main/java/ru/bclib/gui/modmenu/MainScreen.java deleted file mode 100644 index 9e55f8ec..00000000 --- a/src/main/java/ru/bclib/gui/modmenu/MainScreen.java +++ /dev/null @@ -1,93 +0,0 @@ -package ru.bclib.gui.modmenu; - -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; - -import org.jetbrains.annotations.Nullable; -import ru.bclib.config.ConfigKeeper.BooleanEntry; -import ru.bclib.config.Configs; -import ru.bclib.config.NamedPathConfig; -import ru.bclib.config.NamedPathConfig.ConfigTokenDescription; -import ru.bclib.config.NamedPathConfig.DependendConfigToken; -import ru.bclib.gui.gridlayout.GridCheckboxCell; -import ru.bclib.gui.gridlayout.GridColumn; -import ru.bclib.gui.gridlayout.GridRow; -import ru.bclib.gui.gridlayout.GridScreen; -import ru.bclib.gui.gridlayout.GridWidgetWithEnabledState; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -public class MainScreen extends GridScreen{ - - public MainScreen(@Nullable Screen parent) { - super(parent, Component.translatable("title.bclib.modmenu.main"), 10, false); - } - - protected Component getComponent(NamedPathConfig config, ConfigTokenDescription option, String type){ - return Component.translatable(type + ".config." + config.configID + option.getPath() ); - } - - Map> dependentWidgets = new HashMap<>(); - protected void updateEnabledState(){ - dependentWidgets.forEach((cb, supl)->cb.setEnabled(supl.get())); - } - - @SuppressWarnings("unchecked") - protected void addRow(GridColumn grid, NamedPathConfig config, ConfigTokenDescription option){ - if (BooleanEntry.class.isAssignableFrom(option.token.type)) { - addCheckbox(grid, config, (ConfigTokenDescription)option); - } - - grid.addSpacerRow(2); - } - - - protected void addCheckbox(GridColumn grid, NamedPathConfig config, ConfigTokenDescription option){ - if (option.topPadding>0){ - grid.addSpacerRow(option.topPadding); - } - GridRow row = grid.addRow(); - if (option.leftPadding>0){ - row.addSpacer(option.leftPadding); - } - GridCheckboxCell cb = row.addCheckbox(getComponent(config, option, "title"), config.getRaw(option.token), font, (state)-> { - config.set(option.token, state); - updateEnabledState(); - }); - - if (option.token instanceof DependendConfigToken) { - dependentWidgets.put(cb, ()->option.token.dependenciesTrue(config)); - cb.setEnabled(option.token.dependenciesTrue(config)); - } - } - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - protected void initLayout() { - final int BUTTON_HEIGHT = 20; - - Configs.GENERATOR_CONFIG.getAllOptions().stream().filter(o -> !o.hidden).forEach(o -> addRow(grid, Configs.GENERATOR_CONFIG, o)); - grid.addSpacerRow(12); - Configs.MAIN_CONFIG.getAllOptions().stream().filter(o -> !o.hidden).forEach(o -> addRow(grid, Configs.MAIN_CONFIG, o)); - grid.addSpacerRow(12); - Configs.CLIENT_CONFIG.getAllOptions().stream().filter(o -> !o.hidden).forEach(o -> addRow(grid, Configs.CLIENT_CONFIG, o)); - - grid.addSpacerRow(15); - GridRow row = grid.addRow(); - row.addFiller(); - row.addButton(CommonComponents.GUI_DONE, BUTTON_HEIGHT, font, (button)->{ - Configs.CLIENT_CONFIG.saveChanges(); - Configs.GENERATOR_CONFIG.saveChanges(); - Configs.MAIN_CONFIG.saveChanges(); - onClose(); - }); - grid.addSpacerRow(10); - } -} diff --git a/src/main/java/ru/bclib/gui/screens/AtomicProgressListener.java b/src/main/java/ru/bclib/gui/screens/AtomicProgressListener.java deleted file mode 100644 index 6600449d..00000000 --- a/src/main/java/ru/bclib/gui/screens/AtomicProgressListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.bclib.gui.screens; - -import net.minecraft.network.chat.Component; - -public interface AtomicProgressListener { - public void incAtomic(int maxProgress); - public void resetAtomic(); - public void stop(); - public void progressStage(Component component); -} diff --git a/src/main/java/ru/bclib/gui/screens/BCLibScreen.java b/src/main/java/ru/bclib/gui/screens/BCLibScreen.java deleted file mode 100644 index bf95d7c4..00000000 --- a/src/main/java/ru/bclib/gui/screens/BCLibScreen.java +++ /dev/null @@ -1,53 +0,0 @@ -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.Component; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; -import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment; -import ru.bclib.gui.gridlayout.GridRow; -import ru.bclib.gui.gridlayout.GridScreen; - -@Environment(EnvType.CLIENT) -abstract class BCLibScreen extends GridScreen { - static final ResourceLocation BCLIB_LOGO_LOCATION = new ResourceLocation(BCLib.MOD_ID, "icon.png"); - - public BCLibScreen(Component title) { - super(title); - } - - public BCLibScreen(@Nullable Screen parent, Component title){ - super(parent, title); - } - - public BCLibScreen(Component title, int topPadding, boolean centerVertically) { - super(title, topPadding, 20, centerVertically); - } - - public BCLibScreen(@Nullable Screen parent, Component title, int topPadding, boolean centerVertically) { - super(parent, title, topPadding, centerVertically); - } - - public BCLibScreen(Component title, int topPadding, int sidePadding, boolean centerVertically) { - super(title, topPadding, sidePadding, centerVertically); - } - - public BCLibScreen(@Nullable Screen parent, Component title, int topPadding, int sidePadding, boolean centerVertically) { - super(parent, title, topPadding, sidePadding, centerVertically); - } - - - protected void addTitle(){ - GridRow row = grid.addRow(VerticalAlignment.CENTER); - row.addFiller(); - row.addImage(BCLIB_LOGO_LOCATION, 24, GridValueType.CONSTANT, 24, 512, 512); - row.addSpacer(4); - row.addString(this.title, this); - row.addFiller(); - grid.addSpacerRow(15); - } -} diff --git a/src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java b/src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java deleted file mode 100644 index b6aeb0db..00000000 --- a/src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java +++ /dev/null @@ -1,64 +0,0 @@ -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.Component; - -import org.jetbrains.annotations.Nullable; -import ru.bclib.gui.gridlayout.GridCheckboxCell; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridRow; - -@Environment(EnvType.CLIENT) -public class ConfirmFixScreen extends BCLibScreen { - protected final ConfirmFixScreen.Listener listener; - private final Component description; - protected int id; - - public ConfirmFixScreen(@Nullable Screen parent, ConfirmFixScreen.Listener listener) { - super(parent, Component.translatable("bclib.datafixer.backupWarning.title")); - this.listener = listener; - - this.description = Component.translatable("bclib.datafixer.backupWarning.message"); - } - - protected void initLayout() { - final int BUTTON_HEIGHT = 20; - - grid.addRow().addMessage(this.description, this.font, Alignment.CENTER); - grid.addSpacerRow(); - - GridRow row = grid.addRow(); - GridCheckboxCell backup = row.addCheckbox(Component.translatable("bclib.datafixer.backupWarning.backup"), true, BUTTON_HEIGHT, this.font); - - grid.addSpacerRow(10); - - row = grid.addRow(); - GridCheckboxCell fix = row.addCheckbox(Component.translatable("bclib.datafixer.backupWarning.fix"), true, BUTTON_HEIGHT, this.font); - - grid.addSpacerRow(20); - - row = grid.addRow(); - row.addFiller(); - row.addButton(CommonComponents.GUI_CANCEL, BUTTON_HEIGHT, this.font, (button) -> { - onClose(); - }); - row.addSpacer(); - row.addButton(CommonComponents.GUI_PROCEED, BUTTON_HEIGHT, this.font, (button) -> { - this.listener.proceed(backup.isChecked(), fix.isChecked()); - }); - row.addFiller(); - } - - public boolean shouldCloseOnEsc() { - return true; - } - - @Environment(EnvType.CLIENT) - public interface Listener { - void proceed(boolean createBackup, boolean applyPatches); - } -} diff --git a/src/main/java/ru/bclib/gui/screens/ConfirmRestartScreen.java b/src/main/java/ru/bclib/gui/screens/ConfirmRestartScreen.java deleted file mode 100644 index 65361021..00000000 --- a/src/main/java/ru/bclib/gui/screens/ConfirmRestartScreen.java +++ /dev/null @@ -1,51 +0,0 @@ -package ru.bclib.gui.screens; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; - -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridRow; - - -@Environment(EnvType.CLIENT) -public class ConfirmRestartScreen extends BCLibScreen { - private final Component description; - private final ConfirmRestartScreen.Listener listener; - - public ConfirmRestartScreen(ConfirmRestartScreen.Listener listener) { - this(listener, null); - } - - public ConfirmRestartScreen(ConfirmRestartScreen.Listener listener, Component message) { - super(Component.translatable("title.bclib.confirmrestart")); - - this.description = message==null?Component.translatable("message.bclib.confirmrestart"):message; - this.listener = listener; - } - - protected void initLayout() { - final int BUTTON_HEIGHT = 20; - - grid.addRow().addMessage(this.description, this.font, Alignment.CENTER); - - grid.addSpacerRow(); - - GridRow row = grid.addRow(); - row.addFiller(); - row.addButton(CommonComponents.GUI_PROCEED, BUTTON_HEIGHT, font, (button) -> { - listener.proceed(); - }); - row.addFiller(); - } - - public boolean shouldCloseOnEsc() { - return false; - } - - @Environment(EnvType.CLIENT) - public interface Listener { - void proceed(); - } -} diff --git a/src/main/java/ru/bclib/gui/screens/LevelFixErrorScreen.java b/src/main/java/ru/bclib/gui/screens/LevelFixErrorScreen.java deleted file mode 100644 index ffd577af..00000000 --- a/src/main/java/ru/bclib/gui/screens/LevelFixErrorScreen.java +++ /dev/null @@ -1,60 +0,0 @@ -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.Component; - -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, Component.translatable("title.bclib.datafixer.error"), 10, true); - this.errors = errors; - this.onContinue = onContinue; - } - - @Override - protected void initLayout() { - grid.addSpacerRow(); - grid.addRow().addMessage(Component.translatable("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){ - Component dash = Component.literal("-"); - row = col.addRow(); - row.addString(dash, this); - - row.addSpacer(4); - row.addString(Component.literal(error), this); - } - - grid.addSpacerRow(8); - row = grid.addRow(); - row.addFiller(); - row.addButton(Component.translatable("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); - } -} diff --git a/src/main/java/ru/bclib/gui/screens/ModListScreen.java b/src/main/java/ru/bclib/gui/screens/ModListScreen.java deleted file mode 100644 index 93042574..00000000 --- a/src/main/java/ru/bclib/gui/screens/ModListScreen.java +++ /dev/null @@ -1,223 +0,0 @@ -package ru.bclib.gui.screens; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.loader.api.metadata.ModEnvironment; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; -import ru.bclib.api.dataexchange.handler.autosync.HelloClient; -import ru.bclib.gui.gridlayout.GridColumn; -import ru.bclib.gui.gridlayout.GridLayout; -import ru.bclib.gui.gridlayout.GridRow; -import ru.bclib.gui.gridlayout.GridScreen; -import ru.bclib.util.ModUtil; -import ru.bclib.util.PathUtil; -import ru.bclib.util.Triple; - -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.stream.Collectors; - -@Environment(EnvType.CLIENT) -public class ModListScreen extends BCLibScreen { - - private final List mods; - private final HelloClient.IServerModMap serverInfo; - private final Component description; - private final Component buttonTitle; - - private static List extractModList(Map mods){ - List list = new LinkedList(); - ModUtil.getMods().forEach((id, info) -> list.add(info)); - return list; - } - - public ModListScreen(Screen parent, Component title, Component description, Map mods, HelloClient.IServerModMap serverInfo) { - this(parent, title, description, CommonComponents.GUI_BACK, mods, serverInfo); - } - - public ModListScreen(Screen parent, Component title, Component description, List mods, HelloClient.IServerModMap serverInfo) { - this(parent, title, description, CommonComponents.GUI_BACK, mods, serverInfo); - } - - public ModListScreen(Screen parent, Component title, Component description, Component button, Map mods, HelloClient.IServerModMap serverInfo) { - this(parent, title, description, button, extractModList(mods), serverInfo); - } - - public ModListScreen(Screen parent, Component title, Component description, Component button, List mods, HelloClient.IServerModMap serverInfo) { - super(parent, title, 10, true); - this.mods = mods; - this.serverInfo = serverInfo; - this.description = description; - this.buttonTitle = button; - } - - public static List localMissing(HelloClient.IServerModMap serverInfo){ - return serverInfo.keySet() - .stream() - .filter(modid -> !ModUtil.getMods().keySet().stream().filter(mod -> mod.equals(modid)).findFirst().isPresent()).collect(Collectors.toList()); - } - - public static List serverMissing(HelloClient.IServerModMap serverInfo){ - return ModUtil.getMods().entrySet() - .stream() - .filter(entry -> entry.getValue().metadata.getEnvironment() != ModEnvironment.CLIENT) - .map(entry -> entry.getKey()) - .filter(modid -> !serverInfo.keySet().stream().filter(mod -> mod.equals(modid)).findFirst().isPresent()).collect(Collectors.toList()); - } - - - public static void addModDesc(GridColumn grid, java.util.List mods, HelloClient.IServerModMap serverInfo, GridScreen parent) { - final int STATE_OK = 6; - final int STATE_SERVER_MISSING_CLIENT_MOD = 5; - final int STATE_MISSING_NOT_OFFERED = 4; - final int STATE_VERSION_CLIENT_ONLY = 7; - final int STATE_VERSION_NOT_OFFERED = 3; - final int STATE_VERSION = 2; - final int STATE_SERVER_MISSING = 1; - final int STATE_MISSING = 0; - - - List> items = new LinkedList<>(); - if (serverInfo!=null) { - serverInfo.keySet() - .stream() - .filter(modid -> !mods.stream().filter(mod -> mod.metadata.getId().equals(modid)).findFirst().isPresent()) - .forEach(modid -> { - HelloClient.OfferedModInfo nfo = serverInfo.get(modid); - String stateString = nfo.version(); - if (nfo.size()>0) { - stateString = "Version: " + stateString + ", Size: " + PathUtil.humanReadableFileSize(nfo.size()); - } - if (nfo.canDownload()) { - stateString += ", offered by server"; - } - - items.add(new Triple<>(modid, nfo.canDownload()?STATE_MISSING:STATE_MISSING_NOT_OFFERED, stateString)); - }); - } - - mods.forEach(mod -> { - String serverVersion = null; - int serverSize = 0; - int state = STATE_OK; - if (serverInfo != null) { - final String modID = mod.metadata.getId(); - - - HelloClient.OfferedModInfo data = serverInfo.get(modID); - if (data!=null) { - final String modVer = data.version(); - final int size = data.size(); - if (!modVer.equals(mod.getVersion())) { - if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT) - state = STATE_VERSION_CLIENT_ONLY; - else - state = data.canDownload()?STATE_VERSION:STATE_VERSION_NOT_OFFERED; - serverVersion = modVer; - serverSize = size; - } - } else if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT){ - state = STATE_SERVER_MISSING_CLIENT_MOD; - } else { - state = STATE_SERVER_MISSING; - } - } - - String stateString = mod.metadata.getVersion().toString(); - if (serverVersion!=null) { - stateString = "Client: " + stateString; - stateString += ", Server: " + serverVersion; - if (serverSize>0) { - stateString += ", Size: " + PathUtil.humanReadableFileSize(serverSize); - } - } - if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT) { - stateString+= ", client-only"; - } else if (mod.metadata.getEnvironment() == ModEnvironment.SERVER) { - stateString+= ", server-only"; - } - items.add(new Triple<>(mod.metadata.getName(), state, stateString)); - }); - - items.stream() - .sorted(Comparator.comparing(a -> a.second + a.first.toLowerCase(Locale.ROOT))) - .forEach(t -> { - final String name = t.first; - final int state = t.second; - final String stateString = t.third; - - int color = GridLayout.COLOR_RED; - final String typeText; - if (state==STATE_VERSION || state==STATE_VERSION_NOT_OFFERED || state==STATE_VERSION_CLIENT_ONLY) { - typeText = "[VERSION]"; - if (state == STATE_VERSION_NOT_OFFERED) { - color = GridLayout.COLOR_YELLOW; - } else if (state == STATE_VERSION_CLIENT_ONLY) { - color = GridLayout.COLOR_DARK_GREEN; - } - } else if (state==STATE_MISSING || state==STATE_MISSING_NOT_OFFERED) { - typeText = "[MISSING]"; - if (state == STATE_MISSING_NOT_OFFERED) { - color = GridLayout.COLOR_YELLOW; - } - } else if (state==STATE_SERVER_MISSING || state == STATE_SERVER_MISSING_CLIENT_MOD) { - if (state == STATE_SERVER_MISSING_CLIENT_MOD) { - color = GridLayout.COLOR_CYAN; - typeText = "[OK]"; - } else { - typeText = "[NOT ON SERVER]"; - } - } else { - color = GridLayout.COLOR_DARK_GREEN; - typeText = "[OK]"; - } - Component dash = Component.literal("-"); - Component typeTextComponent = Component.literal(typeText); - GridRow row = grid.addRow(); - - row.addString(dash, parent); - - row.addSpacer(4); - row.addString(Component.literal(name), parent); - - row.addSpacer(4); - row.addString(typeTextComponent, color, parent); - - if (!stateString.isEmpty()) { - row = grid.addRow(); - row.addSpacer(4 + parent.getWidth(dash)); - row.addString(Component.literal(stateString), GridLayout.COLOR_GRAY, parent); - } - - grid.addSpacerRow(); - }); - } - - @Override - protected void initLayout() { - if (description != null) { - grid.addSpacerRow(); - grid.addRow().addMessage(description, font, GridLayout.Alignment.CENTER); - grid.addSpacerRow(8); - } - - GridRow row = grid.addRow(); - row.addSpacer(10); - GridColumn col = row.addColumn(200, GridLayout.GridValueType.CONSTANT); - addModDesc(col, mods, serverInfo, this); - - grid.addSpacerRow(8); - row = grid.addRow(); - row.addFiller(); - row.addButton(buttonTitle, 20, font, (n)-> { - onClose(); - }); - row.addFiller(); - } - -} diff --git a/src/main/java/ru/bclib/gui/screens/ProgressScreen.java b/src/main/java/ru/bclib/gui/screens/ProgressScreen.java deleted file mode 100644 index d26c7723..00000000 --- a/src/main/java/ru/bclib/gui/screens/ProgressScreen.java +++ /dev/null @@ -1,196 +0,0 @@ -package ru.bclib.gui.screens; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.gui.GuiComponent; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.network.chat.Component; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.ProgressListener; -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; -import ru.bclib.gui.gridlayout.GridColumn; -import ru.bclib.gui.gridlayout.GridCustomRenderCell; -import ru.bclib.gui.gridlayout.GridLayout; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridLayout.GridValueType; -import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment; -import ru.bclib.gui.gridlayout.GridMessageCell; -import ru.bclib.gui.gridlayout.GridRow; -import ru.bclib.gui.gridlayout.GridScreen; -import ru.bclib.gui.gridlayout.GridStringCell; -import ru.bclib.gui.gridlayout.GridTransform; - -import java.util.concurrent.atomic.AtomicInteger; - -class ProgressLogoRender extends GridCustomRenderCell { - public static final int SIZE = 64; - public static final int LOGO_SIZE = 512; - public static final int PIXELATED_SIZE = 512; - float percentage = 0; - double time = 0; - protected ProgressLogoRender() { - super(SIZE, GridValueType.CONSTANT, SIZE); - } - - @Override - public void onRender(PoseStack poseStack, GridTransform transform, Object context) { - time += 0.03; - RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.enableBlend(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); - - final int yBarLocal = (int)(transform.height*percentage); - final int yBar = transform.top + yBarLocal; - - final float fScale = (float)(0.3*((Math.sin(time)+1.0)*0.5) + 0.7); - int height = (int)(transform.height*fScale); - int width = (int)(transform.width*fScale); - width -= ((transform.width-width)%2); - height -= ((transform.height-height)%2); - - final int yOffset = (transform.height-height)/2; - final int xOffset = (transform.width-width)/2; - - - final int yBarImage = Math.max(0, Math.min(height, yBarLocal - yOffset)); - final float relativeY = ((float)yBarImage/height); - - if (yBarImage>0) { - final int uvTopLogo = (int)(relativeY * LOGO_SIZE); - RenderSystem.setShaderTexture(0, BCLibScreen.BCLIB_LOGO_LOCATION); - GuiComponent.blit(poseStack, - xOffset + transform.left, - yOffset + transform.top, - width, - yBarImage, - 0, 0, LOGO_SIZE, uvTopLogo, - LOGO_SIZE, LOGO_SIZE - ); - } - - if (yBarImage0 && percentage<1.0){ - GuiComponent.fill(poseStack, - transform.left, - yBar, - transform.left+transform.width, - yBar+1, - 0x3FFFFFFF - ); - } - } -} - -public class ProgressScreen extends GridScreen implements ProgressListener, AtomicProgressListener { - - static final ResourceLocation BCLIB_LOGO_PIXELATED_LOCATION = new ResourceLocation(BCLib.MOD_ID, "iconpixelated.png"); - public ProgressScreen(@Nullable Screen parent, Component title, Component description) { - super(parent, title, 20, true); - this.description = description; - } - - - Component description; - private Component stageComponent; - private GridMessageCell stage; - private GridStringCell progress; - private ProgressLogoRender progressImage; - private int currentProgress = 0; - private AtomicInteger atomicCounter; - - @Override - public void incAtomic(int maxProgress) { - if (atomicCounter!=null) { - progressStagePercentage((100*atomicCounter.incrementAndGet())/maxProgress); - } - } - - @Override - public void resetAtomic() { - progressStagePercentage(0); - atomicCounter = new AtomicInteger(0); - } - - public boolean shouldCloseOnEsc() { - return false; - } - - public Component getProgressComponent(){ - return getProgressComponent(currentProgress); - } - - private Component getProgressComponent(int pg){ - return Component.translatable("title.bclib.progress").append(": " + pg + "%"); - } - @Override - protected void initLayout() { - grid.addSpacerRow(); - - GridRow row = grid.addRow(VerticalAlignment.CENTER); - row.addFiller(); - progressImage = new ProgressLogoRender(); - progressImage.percentage = currentProgress / 100.0f; - row.addCustomRender(progressImage); - row.addSpacer(); - - int textWidth = Math.max(getWidth(description), getWidth(getProgressComponent(100))); - GridColumn textCol = row.addColumn(0, GridValueType.INHERIT); - textCol.addRow().addString(description, this); - textCol.addSpacerRow(); - progress = textCol.addRow().addString(getProgressComponent(), GridLayout.COLOR_GRAY, Alignment.LEFT, this); - - row.addFiller(); - - grid.addSpacerRow(20); - row = grid.addRow(); - stage = row.addMessage(stageComponent!=null?stageComponent:Component.literal(""), font, Alignment.CENTER); - } - - @Override - public void progressStartNoAbort(Component text) { - this.progressStage(text); - } - - @Override - public void progressStart(Component text) { - this.progressStage(text); - this.progressStagePercentage(0); - } - - @Override - public void progressStage(Component text) { - stageComponent = text; - if (stage!=null) stage.setText(text); - } - - @Override - public void progressStagePercentage(int progress) { - if (progress!=currentProgress) { - currentProgress = progress; - if (progressImage!=null) progressImage.percentage = currentProgress / 100.0f; - if (this.progress !=null) this.progress.setText(getProgressComponent()); - } - } - - @Override - public void stop() { - - } -} diff --git a/src/main/java/ru/bclib/gui/screens/SyncFilesScreen.java b/src/main/java/ru/bclib/gui/screens/SyncFilesScreen.java deleted file mode 100644 index ca26a86c..00000000 --- a/src/main/java/ru/bclib/gui/screens/SyncFilesScreen.java +++ /dev/null @@ -1,113 +0,0 @@ -package ru.bclib.gui.screens; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; - -import ru.bclib.api.dataexchange.handler.autosync.HelloClient; -import ru.bclib.gui.gridlayout.GridCheckboxCell; -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridRow; -import ru.bclib.util.ModUtil; - -@Environment(EnvType.CLIENT) -public class SyncFilesScreen extends BCLibScreen { - private final Component description; - private final SyncFilesScreen.Listener listener; - private final boolean hasConfigFiles; - private final boolean hasFiles; - private final boolean hasMods; - private final boolean shouldDelete; - private final HelloClient.IServerModMap serverInfo; - public SyncFilesScreen(int modFiles, int configFiles, int singleFiles, int folderFiles, int deleteFiles, HelloClient.IServerModMap serverInfo, Listener listener) { - super(Component.translatable("title.bclib.syncfiles")); - - this.serverInfo = serverInfo; - this.description = Component.translatable("message.bclib.syncfiles"); - this.listener = listener; - - this.hasConfigFiles = configFiles>0; - this.hasFiles = singleFiles+folderFiles>0; - this.hasMods = modFiles>0; - this.shouldDelete = deleteFiles>0; - } - - protected void initLayout() { - final int BUTTON_HEIGHT = 20; - - grid.addRow() - .addMessage(this.description, this.font, Alignment.CENTER); - - grid.addSpacerRow(10); - - GridRow row; - - - final GridCheckboxCell mods; - row = grid.addRow(); - mods = row.addCheckbox(Component.translatable("message.bclib.syncfiles.mods"), hasMods, BUTTON_HEIGHT, this.font); - mods.setEnabled(hasMods); - - row.addSpacer(); - row.addButton(Component.translatable("title.bclib.syncfiles.modInfo"), 20, font, (button)->{ - ModListScreen scr = new ModListScreen( - this, - Component.translatable("title.bclib.syncfiles.modlist"), - Component.translatable("message.bclib.syncfiles.modlist"), - ModUtil.getMods(), - serverInfo - ); - Minecraft.getInstance().setScreen(scr); - }); - - grid.addSpacerRow(); - - - final GridCheckboxCell configs; - row = grid.addRow(); - configs = row.addCheckbox(Component.translatable("message.bclib.syncfiles.configs"), hasConfigFiles, BUTTON_HEIGHT, this.font); - configs.setEnabled(hasConfigFiles); - - grid.addSpacerRow(); - - row = grid.addRow(); - - final GridCheckboxCell folder; - folder = row.addCheckbox(Component.translatable("message.bclib.syncfiles.folders"), hasFiles, BUTTON_HEIGHT, this.font); - folder.setEnabled(hasFiles); - row.addSpacer(); - - GridCheckboxCell delete; - delete = row.addCheckbox(Component.translatable("message.bclib.syncfiles.delete"), shouldDelete, BUTTON_HEIGHT, this.font); - delete.setEnabled(shouldDelete); - - - grid.addSpacerRow(30); - row = grid.addRow(); - row.addFiller(); - row.addButton(CommonComponents.GUI_NO, BUTTON_HEIGHT, this.font, (button) -> { - listener.proceed(false, false, false, false); - }); - row.addSpacer(); - row.addButton(CommonComponents.GUI_YES, BUTTON_HEIGHT, this.font, (button) -> { - listener.proceed( - mods.isChecked(), - configs.isChecked(), - folder.isChecked(), - delete.isChecked() - ); - }); - row.addFiller(); - } - - public boolean shouldCloseOnEsc() { - return false; - } - - @Environment(EnvType.CLIENT) - public interface Listener { - void proceed(boolean downloadMods, boolean downloadConfigs, boolean downloadFiles, boolean removeFiles); - } -} diff --git a/src/main/java/ru/bclib/gui/screens/WarnBCLibVersionMismatch.java b/src/main/java/ru/bclib/gui/screens/WarnBCLibVersionMismatch.java deleted file mode 100644 index e019495a..00000000 --- a/src/main/java/ru/bclib/gui/screens/WarnBCLibVersionMismatch.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.bclib.gui.screens; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; - -import ru.bclib.gui.gridlayout.GridLayout.Alignment; -import ru.bclib.gui.gridlayout.GridRow; - -@Environment(EnvType.CLIENT) -public class WarnBCLibVersionMismatch extends BCLibScreen { - private final Component description; - private final Listener listener; - public WarnBCLibVersionMismatch(Listener listener) { - super(Component.translatable("title.bclib.bclibmissmatch")); - - this.description = Component.translatable("message.bclib.bclibmissmatch"); - this.listener = listener; - } - - protected void initLayout() { - final int BUTTON_HEIGHT = 20; - - grid.addRow().addMessage(this.description, this.font, Alignment.CENTER); - grid.addSpacerRow(20); - GridRow row = grid.addRow(); - row.addFiller(); - row.addButton(CommonComponents.GUI_NO, BUTTON_HEIGHT, this.font, (button) -> { - listener.proceed(false); - }); - row.addSpacer(); - row.addButton(CommonComponents.GUI_YES, BUTTON_HEIGHT, this.font, (button) -> { - listener.proceed(true); - }); - row.addFiller(); - } - - public boolean shouldCloseOnEsc() { - return false; - } - - @Environment(EnvType.CLIENT) - public interface Listener { - void proceed(boolean download); - } -} diff --git a/src/main/java/ru/bclib/integration/ModIntegration.java b/src/main/java/ru/bclib/integration/ModIntegration.java deleted file mode 100644 index 09754517..00000000 --- a/src/main/java/ru/bclib/integration/ModIntegration.java +++ /dev/null @@ -1,211 +0,0 @@ -package ru.bclib.integration; - -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import ru.bclib.BCLib; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.world.features.BCLFeature; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public abstract class ModIntegration { - private final String modID; - - public void init() {} - - public ModIntegration(String modID) { - this.modID = modID; - } - - public ResourceLocation getID(String name) { - return new ResourceLocation(modID, name); - } - - public ResourceKey getFeatureKey(String name) { - return ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, getID(name)); - } - - public Block getBlock(String name) { - return Registry.BLOCK.get(getID(name)); - } - - public Item getItem(String name) { - return Registry.ITEM.get(getID(name)); - } - - public BlockState getDefaultState(String name) { - return getBlock(name).defaultBlockState(); - } - - public ResourceKey getKey(String name) { - return ResourceKey.create(Registry.BIOME_REGISTRY, getID(name)); - } - - public boolean modIsInstalled() { - return FabricLoader.getInstance().isModLoaded(modID); - } - - public BCLFeature getFeature(String featureID, String placedFeatureID, GenerationStep.Decoration featureStep) { - ResourceLocation id = getID(featureID); - Feature feature = Registry.FEATURE.get(id); - Holder featurePlaced = BuiltinRegistries.PLACED_FEATURE.getHolder(getFeatureKey(placedFeatureID)).orElse(null); - return new BCLFeature(id, feature, featureStep, featurePlaced); - } - - public BCLFeature getFeature(String name, GenerationStep.Decoration featureStep) { - return getFeature(name, name, featureStep); - } - - public ConfiguredFeature getConfiguredFeature(String name) { - return BuiltinRegistries.CONFIGURED_FEATURE.get(getID(name)); - } - - public Holder getBiome(String name) { - return BuiltinRegistries.BIOME.getHolder(getKey(name)).orElseThrow(); - } - - public Class getClass(String path) { - Class cl = null; - try { - cl = Class.forName(path); - } - catch (ClassNotFoundException e) { - BCLib.LOGGER.error(e.getMessage()); - if (BCLib.isDevEnvironment()) { - e.printStackTrace(); - } - } - return cl; - } - - @SuppressWarnings("unchecked") - public T getStaticFieldValue(Class cl, String name) { - if (cl != null) { - try { - Field field = cl.getDeclaredField(name); - if (field != null) { - return (T) field.get(null); - } - } - catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } - return null; - } - - public Object getFieldValue(Class cl, String name, Object classInstance) { - if (cl != null) { - try { - Field field = cl.getDeclaredField(name); - if (field != null) { - return field.get(classInstance); - } - } - catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } - return null; - } - - public Method getMethod(Class cl, String functionName, Class... args) { - if (cl != null) { - try { - return cl.getMethod(functionName, args); - } - catch (NoSuchMethodException | SecurityException e) { - BCLib.LOGGER.error(e.getMessage()); - if (BCLib.isDevEnvironment()) { - e.printStackTrace(); - } - } - } - return null; - } - - public Object executeMethod(Object instance, Method method, Object... args) { - if (method != null) { - try { - return method.invoke(instance, args); - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - BCLib.LOGGER.error(e.getMessage()); - if (BCLib.isDevEnvironment()) { - e.printStackTrace(); - } - } - } - return null; - } - - public Object getAndExecuteStatic(Class cl, String functionName, Object... args) { - if (cl != null) { - Class[] classes = new Class[args.length]; - for (int i = 0; i < args.length; i++) { - classes[i] = args[i].getClass(); - } - Method method = getMethod(cl, functionName, classes); - return executeMethod(null, method, args); - } - return null; - } - - @SuppressWarnings("unchecked") - public T getAndExecuteRuntime(Class cl, Object instance, String functionName, Object... args) { - if (instance != null) { - Class[] classes = new Class[args.length]; - for (int i = 0; i < args.length; i++) { - classes[i] = args[i].getClass(); - } - Method method = getMethod(cl, functionName, classes); - return (T) executeMethod(instance, method, args); - } - return null; - } - - public Object newInstance(Class cl, Object... args) { - if (cl != null) { - for (Constructor constructor : cl.getConstructors()) { - if (constructor.getParameterCount() == args.length) { - try { - return constructor.newInstance(args); - } - catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - BCLib.LOGGER.error(e.getMessage()); - if (BCLib.isDevEnvironment()) { - e.printStackTrace(); - } - } - } - } - } - return null; - } - - public TagKey getItemTag(String name) { - ResourceLocation id = getID(name); - return TagAPI.makeItemTag(id); - } - - public TagKey getBlockTag(String name) { - ResourceLocation id = getID(name); - return TagAPI.makeBlockTag(id); - } -} diff --git a/src/main/java/ru/bclib/integration/modmenu/ModMenu.java b/src/main/java/ru/bclib/integration/modmenu/ModMenu.java deleted file mode 100644 index 5d3909bc..00000000 --- a/src/main/java/ru/bclib/integration/modmenu/ModMenu.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.bclib.integration.modmenu; - -import net.minecraft.client.gui.screens.Screen; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -/** - * Integration, to provide a custom Screen for ModMenu. - *

- * This integration allows you to use ModMenu without adding a dependency to your project. If the - * Mod is installed on the Client. - *

- * You can add a screen for your mod by calling {@link #addModMenuScreen(String, Function)} - */ -public class ModMenu { - static final Map> screen = new HashMap<>(); - - /** - * registers a ModMenu entrypoint for another Mod. For Example {@code addModMenuScreen("myMod", (parent)->new Screen(parent));} - * @param modID The ID of your Mod - * @param scr a function that takes a parent {@link Screen} and provides the main Screen you want - * to show with ModMenu for your Mod. - */ - public static void addModMenuScreen(String modID, Function scr) { - screen.put(modID, scr); - } -} diff --git a/src/main/java/ru/bclib/integration/modmenu/ModMenuEntryPoint.java b/src/main/java/ru/bclib/integration/modmenu/ModMenuEntryPoint.java deleted file mode 100644 index 4c8600aa..00000000 --- a/src/main/java/ru/bclib/integration/modmenu/ModMenuEntryPoint.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.bclib.integration.modmenu; - -import com.terraformersmc.modmenu.api.ConfigScreenFactory; -import com.terraformersmc.modmenu.api.ModMenuApi; -import ru.bclib.gui.modmenu.MainScreen; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - - -/** - * Internal class to hook into ModMenu, you should not need to use this class. If you want to register a - * ModMenu Screen for a Mod using BCLib, use {@link ModMenu#addModMenuScreen(String, Function)} - */ -public class ModMenuEntryPoint implements ModMenuApi { - @Override - public Map> getProvidedConfigScreenFactories() { - Map> copy = new HashMap<>(); - for (var entry : ModMenu.screen.entrySet() ){ - copy.put(entry.getKey(), (parent)->entry.getValue().apply(parent)); - } - return copy; - } - - @Override - public ConfigScreenFactory getModConfigScreenFactory() { - return (parent)->new MainScreen(parent); - } - - -} diff --git a/src/main/java/ru/bclib/integration/modmenu/ModMenuIntegration.java b/src/main/java/ru/bclib/integration/modmenu/ModMenuIntegration.java deleted file mode 100644 index 3f449bc3..00000000 --- a/src/main/java/ru/bclib/integration/modmenu/ModMenuIntegration.java +++ /dev/null @@ -1,178 +0,0 @@ -package ru.bclib.integration.modmenu; - -import com.google.common.collect.ImmutableMap; -import com.terraformersmc.modmenu.api.ConfigScreenFactory; -import com.terraformersmc.modmenu.api.ModMenuApi; -import net.minecraft.client.gui.screens.Screen; -import ru.bclib.integration.modmenu.ModMenuIntegration.ModMenuScreenFactory; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Map; - -@Deprecated -class ModMenuScreenFactoryImpl { - record ScreenFactoryInvocationHandler(ModMenuScreenFactory act) implements InvocationHandler { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return switch (method.getName()) { - case "toString" -> ""; - case "equals" -> false; - case "hashCode" -> 0; - default -> act.create((Screen) args[0]); - }; - } - } - - public static ModMenuScreenFactory create(ModMenuScreenFactory act) { - Class iConfigScreenFactory = null; - try { - iConfigScreenFactory = Class.forName("com.terraformersmc.modmenu.api.ConfigScreenFactory"); - } - catch (ClassNotFoundException e) { - e.printStackTrace(); - return null; - } - - Object o = Proxy.newProxyInstance( - ModMenuIntegration.class.getClassLoader(), - new Class[] {iConfigScreenFactory, ModMenuScreenFactory.class}, - new ScreenFactoryInvocationHandler(act)); - - return (ModMenuScreenFactory)o; - } -} - -@Deprecated -/** - * Integration, to provide a custom Screen for ModMenu. - *

- * This integration allows you to use ModMenu without adding a dependency to your project. If the - * Mod is installed on the Client, and the correct ModMenu-EntryPoint is registered in your fabric.mod.json - * the screen will show up. - *

- * You only need to subclass this class, and initialize a static Field of Type {@link ModMenuApi} using - * the {@link #createEntrypoint(ModMenuIntegration)}-Method. - *

- * Example: - *

{@code public class ModMenu extends ModMenuIntegration {
- *	 public static final ModMenuApiMarker entrypointObject = createEntrypoint(new EntryPoint());
- *
- * 		public EntryPoint() {
- * 			super(GridScreen::new);
- *	  }
- * }}
- * You'd also need to add the ModMenu-Entrypoint to your fabric.mod.json: - *
"entrypoints": {
- * 		...
- *	 "modmenu": [ "your.mod.ModMenu::entrypointObject" ]
- * }
- */ -public abstract class ModMenuIntegration { - /** - * Creates a new EntryPoint Object that is accepted by ModMenu - * @param target The delegate Object that will receive calls from ModMenu - * @return A Proxy that conforms to the ModMenu spec - */ - public static ModMenuApi createEntrypoint(ModMenuIntegration target) { - Class iModMenuAPI; - //Class iModMenuAPIMarker = null; - try { - iModMenuAPI = Class.forName("com.terraformersmc.modmenu.api.ModMenuApi"); - //iModMenuAPIMarker = Class.forName("com.terraformersmc.modmenu.util.ModMenuApiMarker"); - } - catch (ClassNotFoundException e) { - e.printStackTrace(); - return null; - } - - Object o = Proxy.newProxyInstance( - ModMenuIntegration.class.getClassLoader(), - new Class[] {iModMenuAPI}, - new BCLibModMenuInvocationHandler(target)); - - return (ModMenuApi) o; - } - - /** - * The factory passed to {@link #ModMenuIntegration(ModMenuScreenFactory)} - */ - protected final ModMenuScreenFactory screenFactory; - - /** - * Create a new ModMenu delegate - * @param screenFactory A Factory. The Factory receives the currently visible {@code parent}-Screen - * and must return a new Screen Object. - */ - public ModMenuIntegration(ModMenuScreenFactory screenFactory){ - this.screenFactory = screenFactory; - } - - /** - * A Helper class to make a BCLib-Factory conform to the ModMenu-Factory Interface. - * @param factory A BCLib-Type Factory, ie. {@code GridScreen::new } - * @return A ModMenu Factory for a Screen - */ - final protected ModMenuScreenFactory createFactory(ModMenuScreenFactory factory){ - return ModMenuScreenFactoryImpl.create( factory ); - } - - /** - * Used to construct a new config screen instance when your mod's - * configuration button is selected on the mod menu screen. The - * screen instance parameter is the active mod menu screen. - * (Text copied from ModMenu) - * - * @return A factory for constructing config screen instances. - * - */ - public ModMenuScreenFactory getModConfigScreenFactory() { - return createFactory(screenFactory); - } - - /** - * Used to provide config screen factories for other mods. This takes second - * priority to a mod's own config screen factory provider. For example, if - * mod `xyz` supplies a config screen factory, mod `abc` providing a config - * screen to `xyz` will be pointless, as the one provided by `xyz` will be - * used. - *

- * This method is NOT meant to be used to add a config screen factory to - * your own mod. - * (Text copied from ModMenu) - * - * @return a map of mod ids to screen factories. - */ - public Map getProvidedConfigScreenFactories() { - return ImmutableMap.of(); - } - - @Override - public String toString() { - return super.toString(); - } - - /** - * A Factory Interface for ModMenu-Screens - *

- * The Interface matches {@code com.terraformersmc.modmenu.api.ConfigScreenFactory} - */ - @FunctionalInterface - public interface ModMenuScreenFactory extends ConfigScreenFactory{} - - record BCLibModMenuInvocationHandler(ModMenuIntegration target) implements InvocationHandler { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return switch (method.getName()) { - case "getModConfigScreenFactory" -> target.getModConfigScreenFactory(); - case "getProvidedConfigScreenFactories" -> target.getProvidedConfigScreenFactories(); - case "toString" -> target.toString(); - case "equals" -> target.equals(args[0]); - case "hashCode" -> target.hashCode(); - default -> null; - }; - } - } - -} diff --git a/src/main/java/ru/bclib/interfaces/AnvilScreenHandlerExtended.java b/src/main/java/ru/bclib/interfaces/AnvilScreenHandlerExtended.java deleted file mode 100644 index c7d3637e..00000000 --- a/src/main/java/ru/bclib/interfaces/AnvilScreenHandlerExtended.java +++ /dev/null @@ -1,36 +0,0 @@ -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 be_getRecipes(); - - default void be_nextRecipe() { - List 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 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)); - } -} diff --git a/src/main/java/ru/bclib/interfaces/BiomeChunk.java b/src/main/java/ru/bclib/interfaces/BiomeChunk.java deleted file mode 100644 index e0848f06..00000000 --- a/src/main/java/ru/bclib/interfaces/BiomeChunk.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.interfaces; - -import ru.bclib.world.generator.BiomePicker; - -public interface BiomeChunk { - void setBiome(int x, int z, BiomePicker.ActualBiome biome); - BiomePicker.ActualBiome getBiome(int x, int z); - int getSide(); -} diff --git a/src/main/java/ru/bclib/interfaces/BiomeMap.java b/src/main/java/ru/bclib/interfaces/BiomeMap.java deleted file mode 100644 index b2ededd6..00000000 --- a/src/main/java/ru/bclib/interfaces/BiomeMap.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.bclib.interfaces; - -import ru.bclib.world.generator.BiomePicker; - -public interface BiomeMap { - void setChunkProcessor(TriConsumer processor); - BiomeChunk getChunk(int cx, int cz, boolean update); - BiomePicker.ActualBiome getBiome(double x, double y, double z); - void clearCache(); -} diff --git a/src/main/java/ru/bclib/interfaces/BiomeSourceAccessor.java b/src/main/java/ru/bclib/interfaces/BiomeSourceAccessor.java deleted file mode 100644 index 356330fd..00000000 --- a/src/main/java/ru/bclib/interfaces/BiomeSourceAccessor.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.bclib.interfaces; - -public interface BiomeSourceAccessor { - void bclRebuildFeatures(); -} diff --git a/src/main/java/ru/bclib/interfaces/BlockModelProvider.java b/src/main/java/ru/bclib/interfaces/BlockModelProvider.java deleted file mode 100644 index fda0f101..00000000 --- a/src/main/java/ru/bclib/interfaces/BlockModelProvider.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.bclib.interfaces; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; - -import java.util.Map; -import java.util.Optional; - -public interface BlockModelProvider extends ItemModelProvider { - @Environment(EnvType.CLIENT) - default @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { - Optional pattern = PatternsHelper.createBlockSimple(resourceLocation); - return ModelsHelper.fromPattern(pattern); - } - - @Environment(EnvType.CLIENT) - default UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { - ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); - registerBlockModel(stateId, modelId, blockState, modelCache); - return ModelsHelper.createBlockSimple(modelId); - } - - @Environment(EnvType.CLIENT) - default void registerBlockModel(ResourceLocation stateId, ResourceLocation modelId, BlockState blockState, Map modelCache) { - if (!modelCache.containsKey(modelId)) { - BlockModel model = getBlockModel(stateId, blockState); - if (model != null) { - model.name = modelId.toString(); - modelCache.put(modelId, model); - } - else { - BCLib.LOGGER.warning("Error loading model: {}", modelId); - } - } - } -} diff --git a/src/main/java/ru/bclib/interfaces/CustomItemProvider.java b/src/main/java/ru/bclib/interfaces/CustomItemProvider.java deleted file mode 100644 index eb1154c3..00000000 --- a/src/main/java/ru/bclib/interfaces/CustomItemProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.bclib.interfaces; - -import net.fabricmc.fabric.api.item.v1.FabricItemSettings; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.BlockItem; - -public interface CustomItemProvider { - /** - * Used to replace default Block Item when block is registered. - * - * @return {@link BlockItem} - */ - BlockItem getCustomItem(ResourceLocation blockID, FabricItemSettings settings); -} diff --git a/src/main/java/ru/bclib/interfaces/ItemModelProvider.java b/src/main/java/ru/bclib/interfaces/ItemModelProvider.java deleted file mode 100644 index 5ef7873f..00000000 --- a/src/main/java/ru/bclib/interfaces/ItemModelProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.bclib.interfaces; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import ru.bclib.client.models.ModelsHelper; - -public interface ItemModelProvider { - @Environment(EnvType.CLIENT) - default BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createItemModel(resourceLocation); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/interfaces/NumericProvider.java b/src/main/java/ru/bclib/interfaces/NumericProvider.java deleted file mode 100644 index 8bd76b02..00000000 --- a/src/main/java/ru/bclib/interfaces/NumericProvider.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.bclib.interfaces; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.Lifecycle; -import net.minecraft.core.MappedRegistry; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; -import ru.bclib.BCLib; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; - -import java.util.function.Function; - -public interface NumericProvider { - ResourceKey>> NUMERIC_PROVIDER_REGISTRY = ResourceKey.createRegistryKey(BCLib.makeID("worldgen/numeric_provider")); - Registry> NUMERIC_PROVIDER = new MappedRegistry<>(NUMERIC_PROVIDER_REGISTRY, Lifecycle.experimental(), null); - Codec CODEC = NUMERIC_PROVIDER.byNameCodec().dispatch(NumericProvider::pcodec, Function.identity()); - int getNumber(SurfaceRulesContextAccessor context); - - Codec pcodec(); -} diff --git a/src/main/java/ru/bclib/interfaces/PatchBiFunction.java b/src/main/java/ru/bclib/interfaces/PatchBiFunction.java deleted file mode 100644 index 4bb345af..00000000 --- a/src/main/java/ru/bclib/interfaces/PatchBiFunction.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.interfaces; - -import ru.bclib.api.datafixer.MigrationProfile; -import ru.bclib.api.datafixer.PatchDidiFailException; - -@FunctionalInterface -public interface PatchBiFunction { - R apply(U t, V v, MigrationProfile profile) throws PatchDidiFailException; -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/interfaces/PatchFunction.java b/src/main/java/ru/bclib/interfaces/PatchFunction.java deleted file mode 100644 index 809d55e0..00000000 --- a/src/main/java/ru/bclib/interfaces/PatchFunction.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.interfaces; - -import ru.bclib.api.datafixer.MigrationProfile; -import ru.bclib.api.datafixer.PatchDidiFailException; - -@FunctionalInterface -public interface PatchFunction { - R apply(T t, MigrationProfile profile) throws PatchDidiFailException; -} diff --git a/src/main/java/ru/bclib/interfaces/PostInitable.java b/src/main/java/ru/bclib/interfaces/PostInitable.java deleted file mode 100644 index 23073c9c..00000000 --- a/src/main/java/ru/bclib/interfaces/PostInitable.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.bclib.interfaces; - -public interface PostInitable { - void postInit(); -} diff --git a/src/main/java/ru/bclib/interfaces/RenderLayerProvider.java b/src/main/java/ru/bclib/interfaces/RenderLayerProvider.java deleted file mode 100644 index 2da7485a..00000000 --- a/src/main/java/ru/bclib/interfaces/RenderLayerProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.bclib.interfaces; - -import ru.bclib.client.render.BCLRenderLayer; - -public interface RenderLayerProvider { - BCLRenderLayer getRenderLayer(); -} diff --git a/src/main/java/ru/bclib/interfaces/SurfaceRuleProvider.java b/src/main/java/ru/bclib/interfaces/SurfaceRuleProvider.java deleted file mode 100644 index d76a8d0e..00000000 --- a/src/main/java/ru/bclib/interfaces/SurfaceRuleProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.bclib.interfaces; - -import net.minecraft.world.level.biome.BiomeSource; - -public interface SurfaceRuleProvider { - void bclib_addBiomeSource(BiomeSource source); - void bclib_clearBiomeSources(); -} diff --git a/src/main/java/ru/bclib/interfaces/TriConsumer.java b/src/main/java/ru/bclib/interfaces/TriConsumer.java deleted file mode 100644 index bdece152..00000000 --- a/src/main/java/ru/bclib/interfaces/TriConsumer.java +++ /dev/null @@ -1,6 +0,0 @@ -package ru.bclib.interfaces; - -@FunctionalInterface -public interface TriConsumer { - void accept(A a, B b, C c); -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineableAxe.java b/src/main/java/ru/bclib/interfaces/tools/AddMineableAxe.java deleted file mode 100644 index e82ee451..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineableAxe.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineableAxe { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineableHammer.java b/src/main/java/ru/bclib/interfaces/tools/AddMineableHammer.java deleted file mode 100644 index a88997bd..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineableHammer.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineableHammer { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineableHoe.java b/src/main/java/ru/bclib/interfaces/tools/AddMineableHoe.java deleted file mode 100644 index 3bd60291..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineableHoe.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineableHoe { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineablePickaxe.java b/src/main/java/ru/bclib/interfaces/tools/AddMineablePickaxe.java deleted file mode 100644 index 92fda1ea..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineablePickaxe.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineablePickaxe { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineableShears.java b/src/main/java/ru/bclib/interfaces/tools/AddMineableShears.java deleted file mode 100644 index 5a42b295..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineableShears.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineableShears { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineableShovel.java b/src/main/java/ru/bclib/interfaces/tools/AddMineableShovel.java deleted file mode 100644 index cdd601f7..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineableShovel.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineableShovel { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/AddMineableSword.java b/src/main/java/ru/bclib/interfaces/tools/AddMineableSword.java deleted file mode 100644 index f68e4500..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/AddMineableSword.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface AddMineableSword { -} diff --git a/src/main/java/ru/bclib/interfaces/tools/PreventMineableAdd.java b/src/main/java/ru/bclib/interfaces/tools/PreventMineableAdd.java deleted file mode 100644 index a6f30fef..00000000 --- a/src/main/java/ru/bclib/interfaces/tools/PreventMineableAdd.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.bclib.interfaces.tools; - -public interface PreventMineableAdd { -} diff --git a/src/main/java/ru/bclib/items/BaseAnvilItem.java b/src/main/java/ru/bclib/items/BaseAnvilItem.java deleted file mode 100644 index f70c754a..00000000 --- a/src/main/java/ru/bclib/items/BaseAnvilItem.java +++ /dev/null @@ -1,73 +0,0 @@ -package ru.bclib.items; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.core.Registry; -import net.minecraft.network.chat.Component; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import org.jetbrains.annotations.Nullable; -import ru.bclib.blocks.BaseAnvilBlock; -import ru.bclib.interfaces.BlockModelProvider; -import ru.bclib.interfaces.ItemModelProvider; - -import java.util.List; -import java.util.Locale; - -public class BaseAnvilItem extends BlockItem implements ItemModelProvider { - public final static String DESTRUCTION = "destruction"; - - public BaseAnvilItem(Block block, Properties properties) { - super(block, properties); - } - - @Override - protected BlockState getPlacementState(BlockPlaceContext blockPlaceContext) { - BlockState blockState = super.getPlacementState(blockPlaceContext); - ItemStack stack = blockPlaceContext.getItemInHand(); - int destruction = stack.getOrCreateTag().getInt(DESTRUCTION); - if (blockState != null) { - BaseAnvilBlock block = (BaseAnvilBlock) blockState.getBlock(); - IntegerProperty durabilityProp = block.getDurabilityProp(); - if (destruction == 0) { - blockState = blockState.setValue(durabilityProp, 0).setValue(BaseAnvilBlock.DESTRUCTION, 0); - } - else { - int destructionValue = destruction / block.getMaxDurability(); - int durabilityValue = destruction - destructionValue * block.getMaxDurability(); - blockState = blockState.setValue(durabilityProp, durabilityValue).setValue(BaseAnvilBlock.DESTRUCTION, destructionValue); - } - } - return blockState; - } - - @Override - @Environment(EnvType.CLIENT) - public void appendHoverText(ItemStack itemStack, @Nullable Level level, List list, TooltipFlag tooltipFlag) { - int destruction = itemStack.getOrCreateTag().getInt(DESTRUCTION); - if (destruction > 0) { - BaseAnvilBlock block = (BaseAnvilBlock) ((BaseAnvilItem) itemStack.getItem()).getBlock(); - int maxValue = block.getMaxDurability() * 3; - float damage = maxValue - destruction; - String percents = String.format(Locale.ROOT, "%.0f%%", damage); - list.add(Component.translatable("message.bclib.anvil_damage").append(": " + percents)); - } - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - Block anvilBlock = getBlock(); - ResourceLocation blockId = Registry.BLOCK.getKey(anvilBlock); - return ((BlockModelProvider) anvilBlock).getBlockModel(blockId, anvilBlock.defaultBlockState()); - } -} diff --git a/src/main/java/ru/bclib/items/BaseArmorItem.java b/src/main/java/ru/bclib/items/BaseArmorItem.java deleted file mode 100644 index 8523d30f..00000000 --- a/src/main/java/ru/bclib/items/BaseArmorItem.java +++ /dev/null @@ -1,61 +0,0 @@ -package ru.bclib.items; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.ai.attributes.Attribute; -import net.minecraft.world.entity.ai.attributes.AttributeModifier; -import net.minecraft.world.entity.ai.attributes.Attributes; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.ArmorMaterial; -import ru.bclib.interfaces.ItemModelProvider; - -import java.util.UUID; - -public class BaseArmorItem extends ArmorItem implements ItemModelProvider { - - protected static final UUID[] ARMOR_MODIFIER_UUID_PER_SLOT = new UUID[] { - UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), - UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), - UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), - UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150") - }; - - protected final Multimap defaultModifiers; - - public BaseArmorItem(ArmorMaterial material, EquipmentSlot equipmentSlot, Properties settings) { - super(material, equipmentSlot, settings); - this.defaultModifiers = HashMultimap.create(); - UUID uuid = ARMOR_MODIFIER_UUID_PER_SLOT[equipmentSlot.getIndex()]; - addAttributeModifier( - Attributes.ARMOR, - new AttributeModifier(uuid, "Armor modifier", getDefense(), AttributeModifier.Operation.ADDITION) - ); - addAttributeModifier( - Attributes.ARMOR_TOUGHNESS, - new AttributeModifier(uuid, "Armor toughness", getToughness(), AttributeModifier.Operation.ADDITION) - ); - if (knockbackResistance > 0.0F) { - addAttributeModifier( - Attributes.KNOCKBACK_RESISTANCE, - new AttributeModifier(uuid, - "Armor knockback resistance", - knockbackResistance, - AttributeModifier.Operation.ADDITION - ) - ); - } - } - - @Override - public Multimap getDefaultAttributeModifiers(EquipmentSlot equipmentSlot) { - return equipmentSlot == slot ? defaultModifiers : super.getDefaultAttributeModifiers(equipmentSlot); - } - - protected void addAttributeModifier(Attribute attribute, AttributeModifier modifier) { - if (defaultModifiers.containsKey(attribute)) { - defaultModifiers.removeAll(attribute); - } - defaultModifiers.put(attribute, modifier); - } -} diff --git a/src/main/java/ru/bclib/items/BaseAttribute.java b/src/main/java/ru/bclib/items/BaseAttribute.java deleted file mode 100644 index 69060830..00000000 --- a/src/main/java/ru/bclib/items/BaseAttribute.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.items; - -import net.minecraft.world.entity.ai.attributes.Attribute; - -public class BaseAttribute extends Attribute { - public BaseAttribute(String description, double value) { - super(description, value); - } -} diff --git a/src/main/java/ru/bclib/items/BaseDiscItem.java b/src/main/java/ru/bclib/items/BaseDiscItem.java deleted file mode 100644 index 17157487..00000000 --- a/src/main/java/ru/bclib/items/BaseDiscItem.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.bclib.items; - -import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.item.RecordItem; -import ru.bclib.interfaces.ItemModelProvider; - -public class BaseDiscItem extends RecordItem implements ItemModelProvider { - public BaseDiscItem(int comparatorOutput, SoundEvent sound, Properties settings) { - super(comparatorOutput, sound, settings); - } -} diff --git a/src/main/java/ru/bclib/items/BaseDrinkItem.java b/src/main/java/ru/bclib/items/BaseDrinkItem.java deleted file mode 100644 index e50818bd..00000000 --- a/src/main/java/ru/bclib/items/BaseDrinkItem.java +++ /dev/null @@ -1,59 +0,0 @@ -package ru.bclib.items; - -import net.minecraft.advancements.CriteriaTriggers; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.stats.Stats; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.ItemUtils; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.UseAnim; -import net.minecraft.world.level.Level; - -public class BaseDrinkItem extends ModelProviderItem { - public BaseDrinkItem(Properties settings) { - super(settings); - } - - @Override - public int getUseDuration(ItemStack stack) { - return 32; - } - - @Override - public UseAnim getUseAnimation(ItemStack stack) { - return UseAnim.DRINK; - } - - @Override - public InteractionResultHolder use(Level world, Player user, InteractionHand hand) { - return ItemUtils.startUsingInstantly(world, user, hand); - } - - @Override - public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity user) { - if (this.isEdible()) { - int count = stack.getCount(); - user.eat(level, stack); - stack.setCount(count); - } - - if (user instanceof ServerPlayer serverPlayerEntity) { - CriteriaTriggers.CONSUME_ITEM.trigger(serverPlayerEntity, stack); - serverPlayerEntity.awardStat(Stats.ITEM_USED.get(this)); - } - - if (user instanceof Player && !((Player) user).getAbilities().instabuild) { - stack.shrink(1); - } - - if (!level.isClientSide) { - user.removeAllEffects(); - } - - return stack.isEmpty() ? new ItemStack(Items.GLASS_BOTTLE) : stack; - } -} diff --git a/src/main/java/ru/bclib/items/BaseSpawnEggItem.java b/src/main/java/ru/bclib/items/BaseSpawnEggItem.java deleted file mode 100644 index b06834e8..00000000 --- a/src/main/java/ru/bclib/items/BaseSpawnEggItem.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.bclib.items; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.item.SpawnEggItem; -import ru.bclib.client.models.BasePatterns; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.client.models.PatternsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -import java.util.Optional; - -public class BaseSpawnEggItem extends SpawnEggItem implements ItemModelProvider { - public BaseSpawnEggItem(EntityType type, int primaryColor, int secondaryColor, Properties settings) { - super(type, primaryColor, secondaryColor, settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - Optional pattern = PatternsHelper.createJson(BasePatterns.ITEM_SPAWN_EGG, resourceLocation); - return ModelsHelper.fromPattern(pattern); - } -} diff --git a/src/main/java/ru/bclib/items/ModelProviderItem.java b/src/main/java/ru/bclib/items/ModelProviderItem.java deleted file mode 100644 index 03b3d1ce..00000000 --- a/src/main/java/ru/bclib/items/ModelProviderItem.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.items; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -public class ModelProviderItem extends Item implements ItemModelProvider { - public ModelProviderItem(Properties settings) { - super(settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createItemModel(resourceLocation); - } -} diff --git a/src/main/java/ru/bclib/items/tool/BaseAxeItem.java b/src/main/java/ru/bclib/items/tool/BaseAxeItem.java deleted file mode 100644 index bcac32bd..00000000 --- a/src/main/java/ru/bclib/items/tool/BaseAxeItem.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.items.tool; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.AxeItem; -import net.minecraft.world.item.Tier; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -public class BaseAxeItem extends AxeItem implements ItemModelProvider { - public BaseAxeItem(Tier material, float attackDamage, float attackSpeed, Properties settings) { - super(material, attackDamage, attackSpeed, settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createHandheldItem(resourceLocation); - } -} diff --git a/src/main/java/ru/bclib/items/tool/BaseHoeItem.java b/src/main/java/ru/bclib/items/tool/BaseHoeItem.java deleted file mode 100644 index 786ed794..00000000 --- a/src/main/java/ru/bclib/items/tool/BaseHoeItem.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.items.tool; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.HoeItem; -import net.minecraft.world.item.Tier; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -public class BaseHoeItem extends HoeItem implements ItemModelProvider { - public BaseHoeItem(Tier material, int attackDamage, float attackSpeed, Properties settings) { - super(material, attackDamage, attackSpeed, settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createHandheldItem(resourceLocation); - } -} diff --git a/src/main/java/ru/bclib/items/tool/BasePickaxeItem.java b/src/main/java/ru/bclib/items/tool/BasePickaxeItem.java deleted file mode 100644 index 5c458249..00000000 --- a/src/main/java/ru/bclib/items/tool/BasePickaxeItem.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.items.tool; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.PickaxeItem; -import net.minecraft.world.item.Tier; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -public class BasePickaxeItem extends PickaxeItem implements ItemModelProvider { - public BasePickaxeItem(Tier material, int attackDamage, float attackSpeed, Properties settings) { - super(material, attackDamage, attackSpeed, settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createHandheldItem(resourceLocation); - } -} diff --git a/src/main/java/ru/bclib/items/tool/BaseShearsItem.java b/src/main/java/ru/bclib/items/tool/BaseShearsItem.java deleted file mode 100644 index 61a29fab..00000000 --- a/src/main/java/ru/bclib/items/tool/BaseShearsItem.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.bclib.items.tool; - - -import net.fabricmc.fabric.api.mininglevel.v1.FabricMineableTags; -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.tag.CommonItemTags; -import ru.bclib.api.tag.TagAPI; - -public class BaseShearsItem extends ShearsItem { - public BaseShearsItem(Properties properties) { - super(properties); - } - - public static boolean isShear(ItemStack tool){ - return tool.is(Items.SHEARS) | tool.is(CommonItemTags.SHEARS) || TagAPI.isToolWithMineableTag(tool, FabricMineableTags.SHEARS_MINEABLE); - } - - public static boolean isShear(ItemStack itemStack, Item item){ - if (item == Items.SHEARS){ - return itemStack.is(item) | itemStack.is(CommonItemTags.SHEARS); - } else { - return itemStack.is(item); - } - } -} diff --git a/src/main/java/ru/bclib/items/tool/BaseShovelItem.java b/src/main/java/ru/bclib/items/tool/BaseShovelItem.java deleted file mode 100644 index 5c92008b..00000000 --- a/src/main/java/ru/bclib/items/tool/BaseShovelItem.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.items.tool; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ShovelItem; -import net.minecraft.world.item.Tier; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -public class BaseShovelItem extends ShovelItem implements ItemModelProvider { - public BaseShovelItem(Tier material, float attackDamage, float attackSpeed, Properties settings) { - super(material, attackDamage, attackSpeed, settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createHandheldItem(resourceLocation); - } -} diff --git a/src/main/java/ru/bclib/items/tool/BaseSwordItem.java b/src/main/java/ru/bclib/items/tool/BaseSwordItem.java deleted file mode 100644 index 18aed8f7..00000000 --- a/src/main/java/ru/bclib/items/tool/BaseSwordItem.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.items.tool; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.SwordItem; -import net.minecraft.world.item.Tier; -import ru.bclib.client.models.ModelsHelper; -import ru.bclib.interfaces.ItemModelProvider; - -public class BaseSwordItem extends SwordItem implements ItemModelProvider { - public BaseSwordItem(Tier material, int attackDamage, float attackSpeed, Properties settings) { - super(material, attackDamage, attackSpeed, settings); - } - - @Override - @Environment(EnvType.CLIENT) - public BlockModel getItemModel(ResourceLocation resourceLocation) { - return ModelsHelper.createHandheldItem(resourceLocation); - } -} diff --git a/src/main/java/ru/bclib/mixin/client/AnvilScreenMixin.java b/src/main/java/ru/bclib/mixin/client/AnvilScreenMixin.java deleted file mode 100644 index bc6d3aa4..00000000 --- a/src/main/java/ru/bclib/mixin/client/AnvilScreenMixin.java +++ /dev/null @@ -1,95 +0,0 @@ -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.Component; -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 { - - @Shadow - private EditBox name; - - private final List 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, Component.literal("<"), b -> be_previousRecipe())); - be_buttons.add(new Button(x + 154, y + 45, 15, 20, Component.literal(">"), 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); - } -} diff --git a/src/main/java/ru/bclib/mixin/client/FogRendererMixin.java b/src/main/java/ru/bclib/mixin/client/FogRendererMixin.java deleted file mode 100644 index 3c798612..00000000 --- a/src/main/java/ru/bclib/mixin/client/FogRendererMixin.java +++ /dev/null @@ -1,62 +0,0 @@ -package ru.bclib.mixin.client; - -import net.minecraft.client.Camera; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.FogRenderer; -import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.material.FogType; -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.client.render.CustomFogRenderer; -import ru.bclib.util.BackgroundInfo; - -@Mixin(FogRenderer.class) -public class FogRendererMixin { - @Shadow - private static float fogRed; - @Shadow - private static float fogGreen; - @Shadow - private static float fogBlue; - - @Inject(method = "setupColor", at = @At("RETURN")) - private static void bclib_onRender(Camera camera, float tickDelta, ClientLevel world, int i, float f, CallbackInfo info) { - FogType fogType = camera.getFluidInCamera(); - if (fogType != FogType.WATER && world.dimension().equals(Level.END)) { - Entity entity = camera.getEntity(); - boolean skip = false; - if (entity instanceof LivingEntity) { - MobEffectInstance effect = ((LivingEntity) entity).getEffect(MobEffects.NIGHT_VISION); - skip = effect != null && effect.getDuration() > 0; - } - if (!skip) { - fogRed *= 4; - fogGreen *= 4; - fogBlue *= 4; - } - } - - BackgroundInfo.fogColorRed = fogRed; - BackgroundInfo.fogColorGreen = fogGreen; - BackgroundInfo.fogColorBlue = fogBlue; - } - - @Inject(method = "setupFog", at = @At("HEAD"), cancellable = true) - private static void bclib_fogDensity(Camera camera, - FogRenderer.FogMode fogMode, - float viewDistance, - boolean thickFog, - float g, - CallbackInfo ci) { - if (CustomFogRenderer.applyFogDensity(camera, viewDistance, thickFog)) { - ci.cancel(); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java b/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java deleted file mode 100644 index b075517e..00000000 --- a/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java +++ /dev/null @@ -1,48 +0,0 @@ -package ru.bclib.mixin.client; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.color.block.BlockColors; -import net.minecraft.client.color.item.ItemColors; -import net.minecraft.client.main.GameConfig; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.server.WorldStem; -import net.minecraft.world.level.LevelSettings; -import net.minecraft.world.level.levelgen.WorldGenSettings; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; -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.CallbackInfo; -import ru.bclib.api.LifeCycleAPI; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.api.datafixer.DataFixerAPI; -import ru.bclib.config.Configs; -import ru.bclib.interfaces.CustomColorProvider; - -import java.util.function.Function; - -@Mixin(Minecraft.class) -public abstract class MinecraftMixin { - @Final - @Shadow - private BlockColors blockColors; - - @Final - @Shadow - private ItemColors itemColors; - - @Inject(method = "*", at = @At("TAIL")) - private void bclib_onMCInit(GameConfig args, CallbackInfo info) { - Registry.BLOCK.forEach(block -> { - if (block instanceof CustomColorProvider provider) { - blockColors.register(provider.getProvider(), block); - itemColors.register(provider.getItemProvider(), block.asItem()); - } - }); - } -} diff --git a/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java b/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java deleted file mode 100644 index 0c78fd30..00000000 --- a/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.bclib.mixin.client; - -import net.minecraft.client.color.block.BlockColors; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.util.profiling.ProfilerFiller; -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.CallbackInfo; -import ru.bclib.api.ModIntegrationAPI; -import ru.bclib.client.models.CustomModelBakery; - -import java.util.Map; - -@Mixin(ModelBakery.class) -public abstract class ModelBakeryMixin { - @Final - @Shadow - private Map unbakedCache; - - @Inject(method = "*", at = @At("TAIL")) - private void bclib_findEmissiveModels(ResourceManager resourceManager, BlockColors blockColors, ProfilerFiller profiler, int mipmap, CallbackInfo info) { - //CustomModelBakery.setModelsLoaded(false); - if (ModIntegrationAPI.hasCanvas()) { - CustomModelBakery.loadEmissiveModels(unbakedCache); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/client/MultiPackResourceManagerMixin.java b/src/main/java/ru/bclib/mixin/client/MultiPackResourceManagerMixin.java deleted file mode 100644 index 3a5a1caa..00000000 --- a/src/main/java/ru/bclib/mixin/client/MultiPackResourceManagerMixin.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.bclib.mixin.client; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.FallbackResourceManager; -import net.minecraft.server.packs.resources.MultiPackResourceManager; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -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.BCLib; -import ru.bclib.api.ModIntegrationAPI; -import ru.bclib.client.render.EmissiveTextureInfo; - -import java.io.IOException; -import java.util.Map; - -@Mixin(MultiPackResourceManager.class) -public class MultiPackResourceManagerMixin { - @Final - @Shadow - private Map namespacedManagers; - - private ResourceLocation bclib_alphaEmissionMaterial = BCLib.makeID("materialmaps/block/alpha_emission.json"); - - @Inject(method = "getResource", at = @At("HEAD"), cancellable = true) - private void bclib_getResource(ResourceLocation resourceLocation, CallbackInfoReturnable info) throws IOException { - if (!ModIntegrationAPI.hasCanvas()) { - return; - } - if (!resourceLocation.getPath().startsWith("materialmaps")) { - return; - } - if (!resourceLocation.getPath().contains("/block/")) { - return; - } - - String name = resourceLocation.getPath().replace("materialmaps/block/", "").replace(".json", ""); - ResourceLocation blockID = new ResourceLocation(resourceLocation.getNamespace(), name); - - if (!EmissiveTextureInfo.isEmissiveBlock(blockID)) { - return; - } - - ResourceManager resourceManager = this.namespacedManagers.get(resourceLocation.getNamespace()); - if (resourceManager != null && resourceManager.getResource(resourceLocation).isEmpty()) { - info.setReturnValue(resourceManager.getResource(bclib_alphaEmissionMaterial).get()); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/client/SignEditScreenMixin.java b/src/main/java/ru/bclib/mixin/client/SignEditScreenMixin.java deleted file mode 100644 index a2772f41..00000000 --- a/src/main/java/ru/bclib/mixin/client/SignEditScreenMixin.java +++ /dev/null @@ -1,62 +0,0 @@ -package ru.bclib.mixin.client; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.inventory.SignEditScreen; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.blockentity.SignRenderer; -import net.minecraft.network.chat.Component; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.SignBlockEntity; -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.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.At.Shift; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import ru.bclib.blocks.BaseSignBlock; -import ru.bclib.client.render.BaseSignBlockEntityRenderer; - -@Mixin(SignEditScreen.class) -public abstract class SignEditScreenMixin extends Screen { - @Shadow @Final private SignBlockEntity sign; - @Shadow private SignRenderer.SignModel signModel; - @Unique private boolean bclib_renderStick; - @Unique private boolean bclib_isSign; - - protected SignEditScreenMixin(Component component) { - super(component); - } - - @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;IIF)V", at = @At( - value = "INVOKE", - target = "Lcom/mojang/blaze3d/vertex/PoseStack;pushPose()V", - shift = Shift.BEFORE - ), locals = LocalCapture.CAPTURE_FAILSOFT) - private void bclib_checkOffset(PoseStack poseStack, int i, int j, float f, CallbackInfo info, float g, BlockState blockState, boolean bl, boolean bl2, float h) { - bclib_isSign = blockState.getBlock() instanceof BaseSignBlock; - if (bclib_isSign) { - bclib_renderStick = blockState.getValue(BaseSignBlock.FLOOR); - if (bclib_renderStick) { - poseStack.translate(0.0, 0.3125, 0.0); - } - } - } - - @ModifyArg(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;IIF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;II)V"), index = 1) - private VertexConsumer bclib_signRender(VertexConsumer consumer) { - if (bclib_isSign) { - signModel.stick.visible = bclib_renderStick; - Block block = sign.getBlockState().getBlock(); - MultiBufferSource.BufferSource bufferSource = this.minecraft.renderBuffers().bufferSource(); - return BaseSignBlockEntityRenderer.getConsumer(bufferSource, block); - } - return consumer; - } -} diff --git a/src/main/java/ru/bclib/mixin/client/TextureAtlasMixin.java b/src/main/java/ru/bclib/mixin/client/TextureAtlasMixin.java deleted file mode 100644 index efdec38a..00000000 --- a/src/main/java/ru/bclib/mixin/client/TextureAtlasMixin.java +++ /dev/null @@ -1,104 +0,0 @@ -package ru.bclib.mixin.client; - -import com.mojang.blaze3d.platform.NativeImage; -import net.fabricmc.fabric.impl.client.texture.FabricSprite; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -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; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import ru.bclib.BCLib; -import ru.bclib.client.render.EmissiveTextureInfo; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Optional; - -@Mixin(TextureAtlas.class) -public class TextureAtlasMixin { - private static final int EMISSIVE_ALPHA = 254 << 24; - private boolean bclib_modifyAtlas; - - @Inject(method = "*", at = @At("TAIL")) - private void bclib_onAtlasInit(ResourceLocation resourceLocation, CallbackInfo info) { - boolean hasOptifine = FabricLoader.getInstance().isModLoaded("optifabric"); - bclib_modifyAtlas = !hasOptifine && resourceLocation.toString().equals("minecraft:textures/atlas/blocks.png"); - if (bclib_modifyAtlas) { - EmissiveTextureInfo.clear(); - } - } - - @Inject(method = "load(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIII)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;", at = @At("HEAD"), cancellable = true) - private void bclib_loadSprite(ResourceManager resourceManager, TextureAtlasSprite.Info spriteInfo, int atlasWidth, int atlasHeight, int maxLevel, int posX, int posY, CallbackInfoReturnable info) { - if (!bclib_modifyAtlas) { - return; - } - - ResourceLocation location = spriteInfo.name(); - if (!location.getPath().startsWith("block")) { - return; - } - - ResourceLocation emissiveLocation = new ResourceLocation( - location.getNamespace(), - "textures/" + location.getPath() + "_e.png" - ); - Optional emissiveRes = resourceManager.getResource(emissiveLocation); - if (emissiveRes.isPresent()) { - NativeImage sprite = null; - NativeImage emission = null; - try { - ResourceLocation spriteLocation = new ResourceLocation( - location.getNamespace(), - "textures/" + location.getPath() + ".png" - ); - Resource resource = resourceManager.getResource(spriteLocation).orElse(null); - sprite = NativeImage.read( resource.open()); - - resource = emissiveRes.get(); - emission = NativeImage.read( resource.open()); - } - catch (IOException e) { - BCLib.LOGGER.warning(e.getMessage()); - } - if (sprite != null && emission != null) { - int width = Math.min(sprite.getWidth(), emission.getWidth()); - int height = Math.min(sprite.getHeight(), emission.getHeight()); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - int argb = emission.getPixelRGBA(x, y); - int alpha = (argb >> 24) & 255; - if (alpha > 127) { - int r = (argb >> 16) & 255; - int g = (argb >> 8) & 255; - int b = argb & 255; - if (r > 0 || g > 0 || b > 0) { - argb = (argb & 0x00FFFFFF) | EMISSIVE_ALPHA; - sprite.setPixelRGBA(x, y, argb); - } - } - } - } - TextureAtlas self = (TextureAtlas) (Object) this; - FabricSprite result = new FabricSprite( - self, - spriteInfo, - maxLevel, - atlasWidth, - atlasHeight, - posX, - posY, - sprite - ); - EmissiveTextureInfo.addTexture(location); - info.setReturnValue(result); - } - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/AnvilBlockMixin.java b/src/main/java/ru/bclib/mixin/common/AnvilBlockMixin.java deleted file mode 100644 index 0c296df3..00000000 --- a/src/main/java/ru/bclib/mixin/common/AnvilBlockMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.world.level.block.AnvilBlock; -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.blocks.BaseAnvilBlock; - -@Mixin(AnvilBlock.class) -public class AnvilBlockMixin { - @Inject(method = "damage", at = @At("HEAD"), cancellable = true) - private static void bclib_anvilDamage(BlockState state, CallbackInfoReturnable info) { - if (state.getBlock() instanceof BaseAnvilBlock) { - BaseAnvilBlock anvil = (BaseAnvilBlock) state.getBlock(); - info.setReturnValue(anvil.damageAnvilFall(state)); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/AnvilMenuMixin.java b/src/main/java/ru/bclib/mixin/common/AnvilMenuMixin.java deleted file mode 100644 index 66bbbcc9..00000000 --- a/src/main/java/ru/bclib/mixin/common/AnvilMenuMixin.java +++ /dev/null @@ -1,213 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.tags.BlockTags; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AnvilMenu; -import net.minecraft.world.inventory.ContainerLevelAccess; -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; -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 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 implements AnvilScreenHandlerExtended { - private List be_recipes = Collections.emptyList(); - private AnvilRecipe be_currentRecipe; - private DataSlot anvilLevel; - - @Shadow - private int repairItemCountCost; - - @Final - @Shadow - private DataSlot cost; - - public AnvilMenuMixin(@Nullable MenuType menuType, int i, Inventory inventory, ContainerLevelAccess containerLevelAccess) { - super(menuType, i, inventory, containerLevelAccess); - } - - @Inject(method = "(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 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) { - info.cancel(); - if (!player.getAbilities().instabuild) { - player.giveExperienceLevels(-this.cost.get()); - } - - this.inputSlots.setItem(0, ItemStack.EMPTY); - if (this.repairItemCountCost > 0) { - ItemStack itemStack2 = this.inputSlots.getItem(1); - if (!itemStack2.isEmpty() && itemStack2.getCount() > this.repairItemCountCost) { - itemStack2.shrink(this.repairItemCountCost); - this.inputSlots.setItem(1, itemStack2); - } - else { - this.inputSlots.setItem(1, ItemStack.EMPTY); - } - } - else { - this.inputSlots.setItem(1, ItemStack.EMPTY); - } - - this.cost.set(0); - - if (!player.getAbilities().instabuild && blockState.is(BlockTags.ANVIL) && player.getRandom().nextFloat() < 0.12F) { - BaseAnvilBlock anvil = (BaseAnvilBlock) blockState.getBlock(); - BlockState damaged = anvil.damageAnvilUse(blockState, player.getRandom()); - if (damaged == null) { - level.removeBlock(blockPos, false); - level.levelEvent(1029, blockPos, 0); - } - else { - level.setBlock(blockPos, damaged, 2); - level.levelEvent(1030, blockPos, 0); - } - } - else { - level.levelEvent(1030, blockPos, 0); - } - } - }); - } - - @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 be_getRecipes() { - return be_recipes; - } -} diff --git a/src/main/java/ru/bclib/mixin/common/BiomeSourceMixin.java b/src/main/java/ru/bclib/mixin/common/BiomeSourceMixin.java deleted file mode 100644 index 5372c899..00000000 --- a/src/main/java/ru/bclib/mixin/common/BiomeSourceMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.bclib.mixin.common; - -import com.google.common.base.Suppliers; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.BiomeSource.StepFeatureData; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.Shadow; -import ru.bclib.BCLib; -import ru.bclib.interfaces.BiomeSourceAccessor; - -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - -@Mixin(BiomeSource.class) -public abstract class BiomeSourceMixin implements BiomeSourceAccessor { - @Shadow protected abstract List buildFeaturesPerStep(List list, boolean bl); - - @Shadow public abstract Set possibleBiomes(); - - @Mutable @Shadow @Final private Supplier> featuresPerStep; - - public void bclRebuildFeatures(){ - BCLib.LOGGER.info("Rebuilding features in BiomeSource " + this); - featuresPerStep = Suppliers.memoize(() -> buildFeaturesPerStep(this.possibleBiomes().stream().toList(), true)); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/BlockStateBaseMixin.java b/src/main/java/ru/bclib/mixin/common/BlockStateBaseMixin.java deleted file mode 100644 index 196e5a4a..00000000 --- a/src/main/java/ru/bclib/mixin/common/BlockStateBaseMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase; -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.util.MethodReplace; - -import java.util.function.Function; - -@Mixin(BlockStateBase.class) -public class BlockStateBaseMixin { - @Inject(method = "is(Lnet/minecraft/world/level/block/Block;)Z", at = @At("HEAD"), cancellable = true) - private void bclib_replaceFunction(Block block, CallbackInfoReturnable info) { - Function replacement = MethodReplace.getBlockReplace(block); - if (replacement != null) { - info.setReturnValue(replacement.apply(BlockStateBase.class.cast(this))); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/BoneMealItemMixin.java b/src/main/java/ru/bclib/mixin/common/BoneMealItemMixin.java deleted file mode 100644 index f3f51ad5..00000000 --- a/src/main/java/ru/bclib/mixin/common/BoneMealItemMixin.java +++ /dev/null @@ -1,164 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.core.Vec3i; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.item.BoneMealItem; -import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -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.BonemealAPI; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.util.BlocksHelper; -import ru.bclib.util.MHelper; - -@Mixin(BoneMealItem.class) -public class BoneMealItemMixin { - private static final MutableBlockPos bclib_BLOCK_POS = new MutableBlockPos(); - - @Inject(method = "useOn", at = @At("HEAD"), cancellable = true) - private void bclib_onUse(UseOnContext context, CallbackInfoReturnable info) { - Level world = context.getLevel(); - BlockPos blockPos = context.getClickedPos(); - if (!world.isClientSide) { - BlockPos offseted = blockPos.relative(context.getClickedFace()); - if (BonemealAPI.isTerrain(world.getBlockState(blockPos).getBlock())) { - boolean consume = false; - if (BonemealAPI.isSpreadableTerrain(world.getBlockState(blockPos).getBlock())) { - BlockState terrain = bclib_getSpreadable(world, blockPos); - if (terrain != null) { - BlocksHelper.setWithoutUpdate(world, blockPos, terrain); - consume = true; - } - } - else { - BlockState stateAbove = world.getBlockState(blockPos.above()); - if (!stateAbove.getFluidState().isEmpty()) { - if (stateAbove.is(Blocks.WATER)) { - consume = bclib_growWaterGrass(world, blockPos); - } - } - else if (stateAbove.isAir()) { - consume = bclib_growLandGrass(world, blockPos); - } - } - if (consume) { - if (!context.getPlayer().isCreative()) { - context.getItemInHand().shrink(1); - } - world.levelEvent(2005, blockPos, 0); - info.setReturnValue(InteractionResult.SUCCESS); - info.cancel(); - } - } - } - } - - private boolean bclib_growLandGrass(Level world, BlockPos pos) { - int y1 = pos.getY() + 3; - int y2 = pos.getY() - 3; - boolean result = false; - for (int i = 0; i < 64; i++) { - int x = (int) (pos.getX() + world.random.nextGaussian() * 2); - int z = (int) (pos.getZ() + world.random.nextGaussian() * 2); - bclib_BLOCK_POS.setX(x); - bclib_BLOCK_POS.setZ(z); - for (int y = y1; y >= y2; y--) { - bclib_BLOCK_POS.setY(y); - BlockPos down = bclib_BLOCK_POS.below(); - if (world.isEmptyBlock(bclib_BLOCK_POS) && !world.isEmptyBlock(down)) { - BlockState grass = bclib_getLandGrassState(world, down); - if (grass != null) { - BlocksHelper.setWithoutUpdate(world, bclib_BLOCK_POS, grass); - result = true; - } - break; - } - } - } - return result; - } - - private boolean bclib_growWaterGrass(Level world, BlockPos pos) { - int y1 = pos.getY() + 3; - int y2 = pos.getY() - 3; - boolean result = false; - for (int i = 0; i < 64; i++) { - int x = (int) (pos.getX() + world.random.nextGaussian() * 2); - int z = (int) (pos.getZ() + world.random.nextGaussian() * 2); - bclib_BLOCK_POS.setX(x); - bclib_BLOCK_POS.setZ(z); - for (int y = y1; y >= y2; y--) { - bclib_BLOCK_POS.setY(y); - BlockPos down = bclib_BLOCK_POS.below(); - if (BlocksHelper.isFluid(world.getBlockState(bclib_BLOCK_POS)) && !BlocksHelper.isFluid(world.getBlockState( - down))) { - BlockState grass = bclib_getWaterGrassState(world, down); - if (grass != null) { - BlocksHelper.setWithoutUpdate(world, bclib_BLOCK_POS, grass); - result = true; - } - break; - } - } - } - return result; - } - - private BlockState bclib_getLandGrassState(Level world, BlockPos pos) { - BlockState state = world.getBlockState(pos); - Block block = state.getBlock(); - block = BonemealAPI.getLandGrass(BiomeAPI.getBiomeID(world.getBiome(pos)), block, world.getRandom()); - return block == null ? null : block.defaultBlockState(); - } - - private BlockState bclib_getWaterGrassState(Level world, BlockPos pos) { - BlockState state = world.getBlockState(pos); - Block block = state.getBlock(); - block = BonemealAPI.getWaterGrass(BiomeAPI.getBiomeID(world.getBiome(pos)), block, world.getRandom()); - return block == null ? null : block.defaultBlockState(); - } - - private BlockState bclib_getSpreadable(Level world, BlockPos pos) { - Vec3i[] offsets = MHelper.getOffsets(world.getRandom()); - BlockState center = world.getBlockState(pos); - for (Vec3i dir : offsets) { - BlockPos p = pos.offset(dir); - BlockState state = world.getBlockState(p); - Block terrain = BonemealAPI.getSpreadable(state.getBlock()); - if (center.is(terrain)) { - if (bclib_haveSameProperties(state, center)) { - for (Property property : center.getProperties()) { - state = state.setValue(property, center.getValue(property)); - } - } - return state; - } - } - return null; - } - - private boolean bclib_haveSameProperties(BlockState state1, BlockState state2) { - Property[] properties1 = state1.getProperties().toArray(new Property[0]); - Property[] properties2 = state2.getProperties().toArray(new Property[0]); - if (properties1.length != properties2.length) { - return false; - } - for (int i = 0; i < properties1.length; i++) { - String name1 = properties1[i].getName(); - String name2 = properties2[i].getName(); - if (!name1.equals(name2)) { - return false; - } - } - return true; - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/mixin/common/ChunkGeneratorMixin.java b/src/main/java/ru/bclib/mixin/common/ChunkGeneratorMixin.java deleted file mode 100644 index 262e1e0a..00000000 --- a/src/main/java/ru/bclib/mixin/common/ChunkGeneratorMixin.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.Registry; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.structure.StructureSet; -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.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import ru.bclib.interfaces.ChunkGeneratorAccessor; - -@Mixin(ChunkGenerator.class) -public class ChunkGeneratorMixin implements ChunkGeneratorAccessor { - @Shadow @Final protected Registry structureSets; - private int bclib_featureIteratorSeed; - - @ModifyArg(method = "applyBiomeDecoration", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/WorldgenRandom;setFeatureSeed(JII)V")) - private long bclib_updateFeatureSeed(long seed) { - return Long.rotateRight(seed, bclib_featureIteratorSeed++); - } - - @Inject(method = "applyBiomeDecoration", at = @At("HEAD")) - private void bclib_obBiomeGenerate(WorldGenLevel worldGenLevel, ChunkAccess chunkAccess, StructureManager structureFeatureManager, CallbackInfo ci) { - bclib_featureIteratorSeed = 0; - } - - public Registry bclib_getStructureSetsRegistry(){ - return structureSets; - } -} diff --git a/src/main/java/ru/bclib/mixin/common/EnchantingTableBlockMixin.java b/src/main/java/ru/bclib/mixin/common/EnchantingTableBlockMixin.java deleted file mode 100644 index da448205..00000000 --- a/src/main/java/ru/bclib/mixin/common/EnchantingTableBlockMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.EnchantmentTableBlock; -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.tag.CommonBlockTags; -import ru.bclib.util.MethodReplace; - -@Mixin(EnchantmentTableBlock.class) -public abstract class EnchantingTableBlockMixin extends Block { - public EnchantingTableBlockMixin(Properties settings) { - super(settings); - } - - @Inject(method = "isValidBookShelf(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/BlockPos;)Z", at = @At("HEAD"), cancellable = true) - private static void bclib_isBookshelf(Level level, BlockPos blockPos, BlockPos blockPos2, CallbackInfoReturnable info) { - MethodReplace.addBlockReplace(Blocks.BOOKSHELF, state -> state.is(CommonBlockTags.BOOKSHELVES)); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/ItemStackMixin.java b/src/main/java/ru/bclib/mixin/common/ItemStackMixin.java deleted file mode 100644 index c52d34fb..00000000 --- a/src/main/java/ru/bclib/mixin/common/ItemStackMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.mixin.common; - -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.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import ru.bclib.util.MethodReplace; - -import java.util.function.Function; - -@Mixin(ItemStack.class) -public class ItemStackMixin { - @Inject(method = "is(Lnet/minecraft/world/item/Item;)Z", at = @At("HEAD"), cancellable = true) - private void bclib_replaceFunction(Item item, CallbackInfoReturnable info) { - Function replacement = MethodReplace.getItemReplace(item); - if (replacement != null) { - info.setReturnValue(replacement.apply(ItemStack.class.cast(this))); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/LayerLightSectionStorageMixin.java b/src/main/java/ru/bclib/mixin/common/LayerLightSectionStorageMixin.java deleted file mode 100644 index a222afef..00000000 --- a/src/main/java/ru/bclib/mixin/common/LayerLightSectionStorageMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.SectionPos; -import net.minecraft.world.level.chunk.DataLayer; -import net.minecraft.world.level.lighting.LayerLightSectionStorage; -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; - -@Mixin(LayerLightSectionStorage.class) -public class LayerLightSectionStorageMixin { - @Shadow - protected DataLayer getDataLayer(long sectionPos, boolean cached) { - return null; - } - - @Inject(method = "getStoredLevel", at = @At(value = "HEAD"), cancellable = true) - private void bclib_lightFix(long blockPos, CallbackInfoReturnable info) { - try { - long pos = SectionPos.blockToSection(blockPos); - DataLayer dataLayer = this.getDataLayer(pos, true); - info.setReturnValue(dataLayer.get( - SectionPos.sectionRelative(BlockPos.getX(blockPos)), - SectionPos.sectionRelative(BlockPos.getY(blockPos)), - SectionPos.sectionRelative(BlockPos.getZ(blockPos)) - )); - } - catch (Exception e) { - info.setReturnValue(0); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/MainMixin.java b/src/main/java/ru/bclib/mixin/common/MainMixin.java deleted file mode 100644 index 7275e583..00000000 --- a/src/main/java/ru/bclib/mixin/common/MainMixin.java +++ /dev/null @@ -1,58 +0,0 @@ -package ru.bclib.mixin.common; - -import joptsimple.ArgumentAcceptingOptionSpec; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import net.minecraft.server.Main; -import net.minecraft.server.dedicated.DedicatedServerSettings; -import net.minecraft.world.level.storage.LevelStorageSource; -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; -import ru.bclib.api.LifeCycleAPI; -import ru.bclib.api.datafixer.DataFixerAPI; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; - -@Mixin(Main.class) -abstract public class MainMixin { - @Inject(method="main", at=@At(value="INVOKE", target="Lnet/minecraft/world/level/storage/LevelStorageSource;createDefault(Ljava/nio/file/Path;)Lnet/minecraft/world/level/storage/LevelStorageSource;")) - private static void bclib_callServerFix(String[] args, CallbackInfo ci){ - OptionParser parser = new OptionParser(); - ArgumentAcceptingOptionSpec optionUniverse = parser.accepts("universe").withRequiredArg().defaultsTo(".", (String[])new String[0]); - ArgumentAcceptingOptionSpec optionWorld = parser.accepts("world").withRequiredArg(); - - //this is only for compat reasons, we do not need to read thise options in our mixin, but it seems to cause - //errors if they are not defined - parser.accepts("nogui"); - parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits"); - parser.accepts("demo"); - parser.accepts("bonusChest"); - parser.accepts("forceUpgrade"); - parser.accepts("eraseCache"); - parser.accepts("safeMode", "Loads level with vanilla datapack only"); - parser.accepts("help").forHelp(); - parser.accepts("singleplayer").withRequiredArg(); - parser.accepts("port").withRequiredArg().ofType(Integer.class).defaultsTo(-1, (Integer[])new Integer[0]); - parser.accepts("serverId").withRequiredArg(); - parser.accepts("jfrProfile"); - parser.nonOptions(); - - OptionSet options = parser.parse(args); - - Path settingPath = Paths.get("server.properties", new String[0]); - DedicatedServerSettings settings = new DedicatedServerSettings(settingPath); - - File file = new File(options.valueOf(optionUniverse)); - String levelID = Optional.ofNullable(options.valueOf(optionWorld)).orElse(settings.getProperties().levelName); - - LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(file.toPath()); - DataFixerAPI.fixData(levelStorageSource, levelID, false, (didFix)->{/* not called when showUI==false */}); - - LifeCycleAPI._runBeforeLevelLoad(); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java b/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java deleted file mode 100644 index e0fd1866..00000000 --- a/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java +++ /dev/null @@ -1,70 +0,0 @@ -package ru.bclib.mixin.common; - -import com.mojang.authlib.GameProfileRepository; -import com.mojang.authlib.minecraft.MinecraftSessionService; -import com.mojang.datafixers.DataFixer; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldStem; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.server.level.progress.ChunkProgressListenerFactory; -import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.server.players.GameProfileCache; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; -import net.minecraft.world.level.storage.WorldData; -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.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import ru.bclib.api.dataexchange.DataExchangeAPI; -import ru.bclib.recipes.BCLRecipeManager; - -import java.net.Proxy; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -@Mixin(MinecraftServer.class) -public class MinecraftServerMixin { - @Shadow - private MinecraftServer.ReloadableResources resources; - - @Final - @Shadow - private Map, ServerLevel> levels; - - @Final - @Shadow - protected WorldData worldData; - - @Inject(method = "*", at = @At("TAIL")) - private void bclib_onServerInit(Thread thread, LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, MinecraftSessionService minecraftSessionService, GameProfileRepository gameProfileRepository, GameProfileCache gameProfileCache, ChunkProgressListenerFactory chunkProgressListenerFactory, CallbackInfo ci) { - DataExchangeAPI.prepareServerside(); - } - - @Inject(method = "reloadResources", at = @At(value = "RETURN"), cancellable = true) - private void bclib_reloadResources(Collection collection, CallbackInfoReturnable> info) { - bclib_injectRecipes(); - } - - @Inject(method = "loadLevel", at = @At(value = "RETURN"), cancellable = true) - private void bclib_loadLevel(CallbackInfo info) { - bclib_injectRecipes(); - } - - private void bclib_injectRecipes() { - RecipeManagerAccessor accessor = (RecipeManagerAccessor) resources.managers().getRecipeManager(); - accessor.bclib_setRecipesByName(BCLRecipeManager.getMapByName(accessor.bclib_getRecipesByName())); - accessor.bclib_setRecipes(BCLRecipeManager.getMap(accessor.bclib_getRecipes())); - } - - @Inject(method = "createLevels", at = @At(value = "HEAD")) - private void bcl_createLevel(ChunkProgressListener chunkProgressListener, CallbackInfo ci){ - System.out.println(this.worldData); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/MultiPackResourceManagerMixin.java b/src/main/java/ru/bclib/mixin/common/MultiPackResourceManagerMixin.java deleted file mode 100644 index 97eb0a6f..00000000 --- a/src/main/java/ru/bclib/mixin/common/MultiPackResourceManagerMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.MultiPackResourceManager; -import net.minecraft.server.packs.resources.Resource; - -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 java.util.Optional; - -@Mixin(MultiPackResourceManager.class) -public class MultiPackResourceManagerMixin { - private static final String[] BCLIB_MISSING_RESOURCES = new String[] { - "dimension/the_end.json", - "dimension/the_nether.json", - "dimension_type/the_end.json", - "dimension_type/the_nether.json" - }; - - @Inject(method = "getResource", at = @At("HEAD"), cancellable = true) - private void bclib_hasResource(ResourceLocation resourceLocation, CallbackInfoReturnable> info) { - if (resourceLocation.getNamespace().equals("minecraft")) { - for (String key: BCLIB_MISSING_RESOURCES) { - if (resourceLocation.getPath().equals(key)) { - info.setReturnValue(Optional.empty()); - info.cancel(); - return; - } - } - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/NoiseBasedChunkGeneratorMixin.java b/src/main/java/ru/bclib/mixin/common/NoiseBasedChunkGeneratorMixin.java deleted file mode 100644 index b4501b90..00000000 --- a/src/main/java/ru/bclib/mixin/common/NoiseBasedChunkGeneratorMixin.java +++ /dev/null @@ -1,63 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.Climate; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.*; -import net.minecraft.world.level.levelgen.blending.Blender; -import net.minecraft.world.level.levelgen.carver.CarvingContext; -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.NoiseGeneratorSettingsProvider; -import ru.bclib.interfaces.SurfaceProvider; - -import java.lang.reflect.Constructor; -import java.util.Optional; - -@Mixin(NoiseBasedChunkGenerator.class) -public abstract class NoiseBasedChunkGeneratorMixin implements SurfaceProvider, NoiseGeneratorSettingsProvider { - @Final - @Shadow - protected Holder settings; - - @Final - @Shadow - private Aquifer.FluidPicker globalFluidPicker; - - private static BlockState bclib_air = Blocks.AIR.defaultBlockState(); - private static Constructor bclib_constructor; - - @Override - public NoiseGeneratorSettings bclib_getNoiseGeneratorSettings(){ - return settings.value(); - } - - @Shadow protected abstract NoiseChunk createNoiseChunk(ChunkAccess chunkAccess, - StructureManager structureManager, - Blender blender, - RandomState randomState); - - @Override - @SuppressWarnings("deprecation") - public BlockState bclib_getSurface(BlockPos pos, Holder biome, ServerLevel level) { - ChunkAccess chunkAccess = level.getChunk(pos.getX() >> 4, pos.getZ() >> 4); - StructureManager structureManager = level.structureManager(); - NoiseBasedChunkGenerator generator = NoiseBasedChunkGenerator.class.cast(this); - RandomState randomState = level.getChunkSource().randomState(); - - NoiseChunk noiseChunk = chunkAccess.getOrCreateNoiseChunk(ca -> this.createNoiseChunk(ca, structureManager, Blender.empty(), randomState)); - - CarvingContext carvingContext = new CarvingContext(generator, level.registryAccess(), chunkAccess.getHeightAccessorForGeneration(), noiseChunk, randomState, this.settings.value().surfaceRule()); - Optional optional = carvingContext.topMaterial(bpos -> biome, chunkAccess, pos, false); - return optional.isPresent() ? optional.get() : bclib_air; - } -} diff --git a/src/main/java/ru/bclib/mixin/common/NoiseGeneratorSettingsMixin.java b/src/main/java/ru/bclib/mixin/common/NoiseGeneratorSettingsMixin.java deleted file mode 100644 index ad9d921d..00000000 --- a/src/main/java/ru/bclib/mixin/common/NoiseGeneratorSettingsMixin.java +++ /dev/null @@ -1,91 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -import net.minecraft.world.level.levelgen.SurfaceRules; -import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.Shadow; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.interfaces.SurfaceRuleProvider; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -@Mixin(NoiseGeneratorSettings.class) -public class NoiseGeneratorSettingsMixin implements SurfaceRuleProvider { - @Mutable - @Final - @Shadow - private SurfaceRules.RuleSource surfaceRule; - - private SurfaceRules.RuleSource bclib_originalSurfaceRule; - private Set bclib_biomeSources = new HashSet<>(); - - private void bclib_updateCustomRules(){ - bclib_setCustomRules(BiomeAPI.getRuleSources(bclib_biomeSources)); - } - - @Override - public void bclib_addBiomeSource(BiomeSource source) { - bclib_biomeSources.add(source); - bclib_updateCustomRules(); - } - - @Override - public void bclib_clearBiomeSources(){ - bclib_biomeSources.clear(); - bclib_clearCustomRules(); - } - - private void bclib_clearCustomRules() { - if (bclib_originalSurfaceRule != null){ - this.surfaceRule = bclib_originalSurfaceRule; - bclib_originalSurfaceRule = null; - } - } - - private void bclib_setCustomRules(List rules) { - if (rules.size()==0){ - bclib_clearCustomRules(); - return; - } - - RuleSource org = bclib_getOriginalSurfaceRule(); - if (org instanceof SurfaceRules.SequenceRuleSource sequenceRule){ - List currentSequence = sequenceRule.sequence(); - rules = rules.stream().filter(r -> currentSequence.indexOf(r)<0).collect(Collectors.toList()); - rules.addAll(sequenceRule.sequence()); - } else { - rules.add(org); - } - - bclib_setSurfaceRule(SurfaceRules.sequence(rules.toArray(new RuleSource[rules.size()]))); - } - - void bclib_setSurfaceRule(SurfaceRules.RuleSource surfaceRule) { - if (bclib_originalSurfaceRule == null){ - bclib_originalSurfaceRule = this.surfaceRule; - } - this.surfaceRule = surfaceRule; - } - - RuleSource bclib_getOriginalSurfaceRule() { - if (bclib_originalSurfaceRule ==null) { - return surfaceRule; - } - - return bclib_originalSurfaceRule; - } - -// @Inject(method = "surfaceRule", at = @At("HEAD"), cancellable = true) -// private void bclib_surfaceRule(CallbackInfoReturnable info) { -// if (bclib_surfaceRule != null) { -// info.setReturnValue(bclib_surfaceRule); -// } -// } -} diff --git a/src/main/java/ru/bclib/mixin/common/PistonBaseBlockMixin.java b/src/main/java/ru/bclib/mixin/common/PistonBaseBlockMixin.java deleted file mode 100644 index 046557ce..00000000 --- a/src/main/java/ru/bclib/mixin/common/PistonBaseBlockMixin.java +++ /dev/null @@ -1,23 +0,0 @@ -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.tag.CommonBlockTags; - -@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 cir){ - if (blockState.is(CommonBlockTags.IMMOBILE)){ - cir.setReturnValue(false); - cir.cancel(); - } - } -} diff --git a/src/main/java/ru/bclib/mixin/common/PortalShapeMixin.java b/src/main/java/ru/bclib/mixin/common/PortalShapeMixin.java deleted file mode 100644 index c9ba4e48..00000000 --- a/src/main/java/ru/bclib/mixin/common/PortalShapeMixin.java +++ /dev/null @@ -1,33 +0,0 @@ -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.tag.CommonBlockTags; - -@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(CommonBlockTags.NETHER_PORTAL_FRAME) || FRAME.test(state, getter, pos); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/RecipeManagerAccessor.java b/src/main/java/ru/bclib/mixin/common/RecipeManagerAccessor.java deleted file mode 100644 index 37625cfe..00000000 --- a/src/main/java/ru/bclib/mixin/common/RecipeManagerAccessor.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.crafting.Recipe; -import net.minecraft.world.item.crafting.RecipeManager; -import net.minecraft.world.item.crafting.RecipeType; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Map; - -@Mixin(RecipeManager.class) -public interface RecipeManagerAccessor { - @Accessor("recipes") - Map, Map>> bclib_getRecipes(); - - @Accessor("recipes") - void bclib_setRecipes(Map, Map>> recipes); - - @Accessor("byName") - Map> bclib_getRecipesByName(); - - @Accessor("byName") - void bclib_setRecipesByName(Map> recipes); -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/mixin/common/RecipeManagerMixin.java b/src/main/java/ru/bclib/mixin/common/RecipeManagerMixin.java deleted file mode 100644 index a9b6ea63..00000000 --- a/src/main/java/ru/bclib/mixin/common/RecipeManagerMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.Container; -import net.minecraft.world.item.crafting.Recipe; -import net.minecraft.world.item.crafting.RecipeManager; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.Level; -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.recipes.BCLRecipeManager; - -import java.util.Map; -import java.util.Optional; - -@Mixin(RecipeManager.class) -public abstract class RecipeManagerMixin { - @Shadow - private > Map> byType(RecipeType type) { - return null; - } - - @Inject(method = "getRecipeFor", at = @At(value = "HEAD"), cancellable = true) - private > void bclib_getRecipeFor(RecipeType type, C inventory, Level level, CallbackInfoReturnable> info) { - info.setReturnValue(BCLRecipeManager.getSortedRecipe(type, inventory, level, this::byType)); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/mixin/common/ServerLevelMixin.java b/src/main/java/ru/bclib/mixin/common/ServerLevelMixin.java deleted file mode 100644 index c9671a7a..00000000 --- a/src/main/java/ru/bclib/mixin/common/ServerLevelMixin.java +++ /dev/null @@ -1,77 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; -import net.minecraft.world.level.storage.ServerLevelData; -import net.minecraft.world.level.storage.WritableLevelData; -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; -import ru.bclib.api.LifeCycleAPI; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.world.generator.BCLBiomeSource; -import ru.bclib.world.generator.BCLibNetherBiomeSource; - -import java.util.List; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -@Mixin(ServerLevel.class) -public abstract class ServerLevelMixin extends Level { - private static String bclib_lastWorld = null; - - protected ServerLevelMixin(WritableLevelData writableLevelData, - ResourceKey resourceKey, - Holder holder, - Supplier supplier, - boolean bl, - boolean bl2, - long l, - int i) { - super(writableLevelData, resourceKey, holder, supplier, bl, bl2, l, i); - } - - - @Inject(method = "*", at = @At("TAIL")) - private void bclib_onServerWorldInit(MinecraftServer server, - Executor executor, - LevelStorageAccess levelStorageAccess, - ServerLevelData serverLevelData, - ResourceKey resourceKey, - LevelStem levelStem, - ChunkProgressListener chunkProgressListener, - boolean bl, - long l, - List list, - boolean bl2, - CallbackInfo ci) { - ServerLevel level = ServerLevel.class.cast(this); - LifeCycleAPI._runLevelLoad(level, server, executor, levelStorageAccess, serverLevelData, resourceKey, chunkProgressListener, bl, l, list, bl2); - - BiomeAPI.applyModifications(ServerLevel.class.cast(this)); - - if (level.dimension() == Level.NETHER) { - BCLibNetherBiomeSource.setWorldHeight(level.getChunkSource().getGenerator().getGenDepth()); - } - if (levelStem.generator().getBiomeSource() instanceof BCLBiomeSource source){ - source.setSeed(level.getSeed()); - } - - if (bclib_lastWorld != null && bclib_lastWorld.equals(levelStorageAccess.getLevelId())) { - return; - } - - bclib_lastWorld = levelStorageAccess.getLevelId(); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/SurfaceRulesContextAccessor.java b/src/main/java/ru/bclib/mixin/common/SurfaceRulesContextAccessor.java deleted file mode 100644 index 9a0b11fb..00000000 --- a/src/main/java/ru/bclib/mixin/common/SurfaceRulesContextAccessor.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.Holder; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.NoiseChunk; -import net.minecraft.world.level.levelgen.SurfaceRules; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.function.Supplier; - -@Mixin(SurfaceRules.Context.class) -public interface SurfaceRulesContextAccessor { - @Accessor("blockX") - int getBlockX(); - - @Accessor("blockY") - int getBlockY(); - - @Accessor("blockZ") - int getBlockZ(); - - @Accessor("surfaceDepth") - int getSurfaceDepth(); - - @Accessor("biome") - Supplier> getBiome(); - - @Accessor("chunk") - ChunkAccess getChunk(); - - @Accessor("noiseChunk") - NoiseChunk getNoiseChunk(); - - @Accessor("stoneDepthAbove") - int getStoneDepthAbove(); - - @Accessor("stoneDepthBelow") - int getStoneDepthBelow(); - - @Accessor("lastUpdateY") - long getLastUpdateY(); - - @Accessor("lastUpdateXZ") - long getLastUpdateXZ(); -} diff --git a/src/main/java/ru/bclib/mixin/common/TagLoaderMixin.java b/src/main/java/ru/bclib/mixin/common/TagLoaderMixin.java deleted file mode 100644 index 46b83f7a..00000000 --- a/src/main/java/ru/bclib/mixin/common/TagLoaderMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.Tag; -import net.minecraft.tags.TagLoader; -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.ModifyArg; -import ru.bclib.api.tag.TagAPI; - -import java.util.Map; - -@Mixin(TagLoader.class) -public class TagLoaderMixin { - @Final - @Shadow - private String directory; - - @ModifyArg(method = "loadAndBuild", at = @At(value = "INVOKE", target = "Lnet/minecraft/tags/TagLoader;build(Ljava/util/Map;)Ljava/util/Map;")) - public Map be_modifyTags(Map tagsMap) { - return TagAPI.apply(directory, tagsMap); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/TheEndBiomeDataMixin.java b/src/main/java/ru/bclib/mixin/common/TheEndBiomeDataMixin.java deleted file mode 100644 index ee60a786..00000000 --- a/src/main/java/ru/bclib/mixin/common/TheEndBiomeDataMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.bclib.mixin.common; - -import net.fabricmc.fabric.impl.biome.TheEndBiomeData; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.Biomes; -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; -import ru.bclib.world.biomes.FabricBiomesData; - -@Mixin(value = TheEndBiomeData.class, remap = false) -public class TheEndBiomeDataMixin { - @Inject(method = "addEndBiomeReplacement", at = @At(value = "HEAD")) - private static void bclib_addEndBiomeReplacement(ResourceKey replaced, ResourceKey variant, double weight, CallbackInfo info) { - if (replaced == Biomes.END_BARRENS || replaced == Biomes.SMALL_END_ISLANDS) { - FabricBiomesData.END_VOID_BIOMES.put(variant, (float) weight); - } - else { - FabricBiomesData.END_LAND_BIOMES.put(variant, (float) weight); - } - } - - @Inject(method = "addEndMidlandsReplacement", at = @At(value = "HEAD")) - private static void bclib_addEndMidlandsReplacement(ResourceKey highlands, ResourceKey midlands, double weight, CallbackInfo info) { - FabricBiomesData.END_LAND_BIOMES.put(midlands, (float) weight); - } - - @Inject(method = "addEndBarrensReplacement", at = @At(value = "HEAD")) - private static void bclib_addEndBarrensReplacement(ResourceKey highlands, ResourceKey barrens, double weight, CallbackInfo info) { - FabricBiomesData.END_LAND_BIOMES.put(barrens, (float) weight); - FabricBiomesData.END_VOID_BIOMES.put(barrens, (float) weight); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/WorldGenRegionMixin.java b/src/main/java/ru/bclib/mixin/common/WorldGenRegionMixin.java deleted file mode 100644 index 29a130c5..00000000 --- a/src/main/java/ru/bclib/mixin/common/WorldGenRegionMixin.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.bclib.mixin.common; - -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.world.level.chunk.ChunkAccess; -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; - -@Mixin(WorldGenRegion.class) -public class WorldGenRegionMixin { - @Final - @Shadow - private ChunkAccess center; - - @Inject(method = "ensureCanWrite", at = @At("HEAD"), cancellable = true) - private void be_alterBlockCheck(BlockPos blockPos, CallbackInfoReturnable info) { - int x = blockPos.getX() >> 4; - int z = blockPos.getZ() >> 4; - WorldGenRegion region = (WorldGenRegion) (Object) this; - info.setReturnValue(Math.abs(x - center.getPos().x) < 2 && Math.abs(z - center.getPos().z) < 2); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/shears/BeehiveBlockMixin.java b/src/main/java/ru/bclib/mixin/common/shears/BeehiveBlockMixin.java deleted file mode 100644 index c39e4f92..00000000 --- a/src/main/java/ru/bclib/mixin/common/shears/BeehiveBlockMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.bclib.mixin.common.shears; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.BeehiveBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.BlockHitResult; -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.items.tool.BaseShearsItem; -import ru.bclib.util.MethodReplace; - -@Mixin(BeehiveBlock.class) -public class BeehiveBlockMixin { - @Inject(method = "use(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) - private void bclib_isShears(BlockState blockState, Level level, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable info) { - MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/shears/MushroomCowMixin.java b/src/main/java/ru/bclib/mixin/common/shears/MushroomCowMixin.java deleted file mode 100644 index 9b454932..00000000 --- a/src/main/java/ru/bclib/mixin/common/shears/MushroomCowMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.mixin.common.shears; - -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.animal.MushroomCow; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Items; -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.items.tool.BaseShearsItem; -import ru.bclib.util.MethodReplace; - -@Mixin(MushroomCow.class) -public class MushroomCowMixin { - @Inject(method = "mobInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) - private void bclib_isShears(Player player, InteractionHand interactionHand, CallbackInfoReturnable info) { - MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/shears/SheepMixin.java b/src/main/java/ru/bclib/mixin/common/shears/SheepMixin.java deleted file mode 100644 index d4205297..00000000 --- a/src/main/java/ru/bclib/mixin/common/shears/SheepMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.mixin.common.shears; - -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.animal.Sheep; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Items; -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.items.tool.BaseShearsItem; -import ru.bclib.util.MethodReplace; - -@Mixin(Sheep.class) -public class SheepMixin { - @Inject(method = "mobInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) - private void bclib_isShears(Player player, InteractionHand interactionHand, CallbackInfoReturnable info) { - MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/shears/SnowGolemMixin.java b/src/main/java/ru/bclib/mixin/common/shears/SnowGolemMixin.java deleted file mode 100644 index 691ac2c1..00000000 --- a/src/main/java/ru/bclib/mixin/common/shears/SnowGolemMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.mixin.common.shears; - -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.animal.SnowGolem; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Items; -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.items.tool.BaseShearsItem; -import ru.bclib.util.MethodReplace; - -@Mixin(SnowGolem.class) -public class SnowGolemMixin { - @Inject(method = "mobInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) - private void bclib_isShears(Player player, InteractionHand interactionHand, CallbackInfoReturnable info) { - MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); - } -} diff --git a/src/main/java/ru/bclib/mixin/common/shears/TripWireBlockMixin.java b/src/main/java/ru/bclib/mixin/common/shears/TripWireBlockMixin.java deleted file mode 100644 index 80c3d886..00000000 --- a/src/main/java/ru/bclib/mixin/common/shears/TripWireBlockMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.mixin.common.shears; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.TripWireBlock; -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.CallbackInfo; -import ru.bclib.items.tool.BaseShearsItem; -import ru.bclib.util.MethodReplace; - -@Mixin(TripWireBlock.class) -public class TripWireBlockMixin { - @Inject(method = "playerWillDestroy(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/player/Player;)V", at = @At("HEAD")) - private void bclib_isShears(Level level, BlockPos blockPos, BlockState blockState, Player player, CallbackInfo info) { - MethodReplace.addItemReplace(Items.SHEARS, BaseShearsItem::isShear); - } -} diff --git a/src/main/java/ru/bclib/noise/OpenSimplexNoise.java b/src/main/java/ru/bclib/noise/OpenSimplexNoise.java deleted file mode 100644 index 9a59f424..00000000 --- a/src/main/java/ru/bclib/noise/OpenSimplexNoise.java +++ /dev/null @@ -1,2671 +0,0 @@ -package ru.bclib.noise; - -/* - * OpenSimplex Noise in Java. - * by Kurt Spencer - * - * v1.1 (October 5, 2014) - * - Added 2D and 4D implementations. - * - Proper gradient sets for all dimensions, from a - * dimensionally-generalizable scheme with an actual - * rhyme and reason behind it. - * - Removed default permutation array in favor of - * default seed. - * - Changed seed-based constructor to be independent - * of any particular randomization library, so results - * will be the same when ported to other languages. - */ - -public class OpenSimplexNoise { - private static final double STRETCH_CONSTANT_2D = -0.211324865405187; // (1/Math.sqrt(2+1)-1)/2; - private static final double SQUISH_CONSTANT_2D = 0.366025403784439; // (Math.sqrt(2+1)-1)/2; - private static final double STRETCH_CONSTANT_3D = -1.0 / 6; // (1/Math.sqrt(3+1)-1)/3; - private static final double SQUISH_CONSTANT_3D = 1.0 / 3; // (Math.sqrt(3+1)-1)/3; - private static final double STRETCH_CONSTANT_4D = -0.138196601125011; // (1/Math.sqrt(4+1)-1)/4; - private static final double SQUISH_CONSTANT_4D = 0.309016994374947; // (Math.sqrt(4+1)-1)/4; - - private static final double NORM_CONSTANT_2D = 47; - private static final double NORM_CONSTANT_3D = 103; - private static final double NORM_CONSTANT_4D = 30; - - private static final long DEFAULT_SEED = 0; - - private short[] perm; - private short[] permGradIndex3D; - - public OpenSimplexNoise() { - this(DEFAULT_SEED); - } - - public OpenSimplexNoise(short[] perm) { - this.perm = perm; - permGradIndex3D = new short[256]; - - for (int i = 0; i < 256; i++) { - // Since 3D has 24 gradients, simple bitmask won't work, so - // precompute modulo array. - permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3); - } - } - - // Initializes the class using a permutation array generated from a 64-bit - // seed. - // Generates a proper permutation (i.e. doesn't merely perform N successive - // pair swaps on a base array) - // Uses a simple 64-bit LCG. - public OpenSimplexNoise(long seed) { - perm = new short[256]; - permGradIndex3D = new short[256]; - short[] source = new short[256]; - for (short i = 0; i < 256; i++) { - source[i] = i; - } - seed = seed * 6364136223846793005l + 1442695040888963407l; - seed = seed * 6364136223846793005l + 1442695040888963407l; - seed = seed * 6364136223846793005l + 1442695040888963407l; - for (int i = 255; i >= 0; i--) { - seed = seed * 6364136223846793005l + 1442695040888963407l; - int r = (int) ((seed + 31) % (i + 1)); - if (r < 0) r += (i + 1); - perm[i] = source[r]; - permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3); - source[r] = source[i]; - } - } - - // 2D OpenSimplex Noise. - public double eval(double x, double y) { - - // Place input coordinates onto grid. - double stretchOffset = (x + y) * STRETCH_CONSTANT_2D; - double xs = x + stretchOffset; - double ys = y + stretchOffset; - - // Floor to get grid coordinates of rhombus (stretched square) - // super-cell origin. - int xsb = fastFloor(xs); - int ysb = fastFloor(ys); - - // Skew out to get actual coordinates of rhombus origin. We'll need - // these later. - double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D; - double xb = xsb + squishOffset; - double yb = ysb + squishOffset; - - // Compute grid coordinates relative to rhombus origin. - double xins = xs - xsb; - double yins = ys - ysb; - - // Sum those together to get a value that determines which region we're - // in. - double inSum = xins + yins; - - // Positions relative to origin point. - double dx0 = x - xb; - double dy0 = y - yb; - - // We'll be defining these inside the next block and using them - // afterwards. - double dx_ext, dy_ext; - int xsv_ext, ysv_ext; - - double value = 0; - - // Contribution (1,0) - double dx1 = dx0 - 1 - SQUISH_CONSTANT_2D; - double dy1 = dy0 - 0 - SQUISH_CONSTANT_2D; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1); - } - - // Contribution (0,1) - double dx2 = dx0 - 0 - SQUISH_CONSTANT_2D; - double dy2 = dy0 - 1 - SQUISH_CONSTANT_2D; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2); - } - - if (inSum <= 1) { // We're inside the triangle (2-Simplex) at (0,0) - double zins = 1 - inSum; - if (zins > xins || zins > yins) { // (0,0) is one of the closest two - // triangular vertices - if (xins > yins) { - xsv_ext = xsb + 1; - ysv_ext = ysb - 1; - dx_ext = dx0 - 1; - dy_ext = dy0 + 1; - } - else { - xsv_ext = xsb - 1; - ysv_ext = ysb + 1; - dx_ext = dx0 + 1; - dy_ext = dy0 - 1; - } - } - else { // (1,0) and (0,1) are the closest two vertices. - xsv_ext = xsb + 1; - ysv_ext = ysb + 1; - dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; - dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; - } - } - else { // We're inside the triangle (2-Simplex) at (1,1) - double zins = 2 - inSum; - if (zins < xins || zins < yins) { // (0,0) is one of the closest two - // triangular vertices - if (xins > yins) { - xsv_ext = xsb + 2; - ysv_ext = ysb + 0; - dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D; - dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D; - } - else { - xsv_ext = xsb + 0; - ysv_ext = ysb + 2; - dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D; - dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D; - } - } - else { // (1,0) and (0,1) are the closest two vertices. - dx_ext = dx0; - dy_ext = dy0; - xsv_ext = xsb; - ysv_ext = ysb; - } - xsb += 1; - ysb += 1; - dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; - dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; - } - - // Contribution (0,0) or (1,1) - double attn0 = 2 - dx0 * dx0 - dy0 * dy0; - if (attn0 > 0) { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0); - } - - // Extra Vertex - double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext; - if (attn_ext > 0) { - attn_ext *= attn_ext; - value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext); - } - - return value / NORM_CONSTANT_2D; - } - - // 3D OpenSimplex Noise. - public double eval(double x, double y, double z) { - - // Place input coordinates on simplectic honeycomb. - double stretchOffset = (x + y + z) * STRETCH_CONSTANT_3D; - double xs = x + stretchOffset; - double ys = y + stretchOffset; - double zs = z + stretchOffset; - - // Floor to get simplectic honeycomb coordinates of rhombohedron - // (stretched cube) super-cell origin. - int xsb = fastFloor(xs); - int ysb = fastFloor(ys); - int zsb = fastFloor(zs); - - // Skew out to get actual coordinates of rhombohedron origin. We'll need - // these later. - double squishOffset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D; - double xb = xsb + squishOffset; - double yb = ysb + squishOffset; - double zb = zsb + squishOffset; - - // Compute simplectic honeycomb coordinates relative to rhombohedral - // origin. - double xins = xs - xsb; - double yins = ys - ysb; - double zins = zs - zsb; - - // Sum those together to get a value that determines which region we're - // in. - double inSum = xins + yins + zins; - - // Positions relative to origin point. - double dx0 = x - xb; - double dy0 = y - yb; - double dz0 = z - zb; - - // We'll be defining these inside the next block and using them - // afterwards. - double dx_ext0, dy_ext0, dz_ext0; - double dx_ext1, dy_ext1, dz_ext1; - int xsv_ext0, ysv_ext0, zsv_ext0; - int xsv_ext1, ysv_ext1, zsv_ext1; - - double value = 0; - if (inSum <= 1) { // We're inside the tetrahedron (3-Simplex) at (0,0,0) - - // Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. - byte aPoint = 0x01; - double aScore = xins; - byte bPoint = 0x02; - double bScore = yins; - if (aScore >= bScore && zins > bScore) { - bScore = zins; - bPoint = 0x04; - } - else if (aScore < bScore && zins > aScore) { - aScore = zins; - aPoint = 0x04; - } - - // Now we determine the two lattice points not part of the - // tetrahedron that may contribute. - // This depends on the closest two tetrahedral vertices, including - // (0,0,0) - double wins = 1 - inSum; - if (wins > aScore || wins > bScore) { // (0,0,0) is one of the - // closest two tetrahedral - // vertices. - byte c = (bScore > aScore ? bPoint : aPoint); // Our other - // closest - // vertex is the - // closest out - // of a and b. - - if ((c & 0x01) == 0) { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsb; - dx_ext0 = dx0 + 1; - dx_ext1 = dx0; - } - else { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx_ext1 = dx0 - 1; - } - - if ((c & 0x02) == 0) { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0; - if ((c & 0x01) == 0) { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - else { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1; - } - - if ((c & 0x04) == 0) { - zsv_ext0 = zsb; - zsv_ext1 = zsb - 1; - dz_ext0 = dz0; - dz_ext1 = dz0 + 1; - } - else { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1; - } - } - else { // (0,0,0) is not one of the closest two tetrahedral - // vertices. - byte c = (byte) (aPoint | bPoint); // Our two extra vertices are - // determined by the closest - // two. - - if ((c & 0x01) == 0) { - xsv_ext0 = xsb; - xsv_ext1 = xsb - 1; - dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D; - dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; - } - else { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; - dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; - } - - if ((c & 0x02) == 0) { - ysv_ext0 = ysb; - ysv_ext1 = ysb - 1; - dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D; - dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; - } - else { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; - } - - if ((c & 0x04) == 0) { - zsv_ext0 = zsb; - zsv_ext1 = zsb - 1; - dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; - } - else { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; - } - } - - // Contribution (0,0,0) - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; - if (attn0 > 0) { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0); - } - - // Contribution (1,0,0) - double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; - double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; - double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); - } - - // Contribution (0,1,0) - double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; - double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; - double dz2 = dz1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); - } - - // Contribution (0,0,1) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); - } - } - else if (inSum >= 2) { // We're inside the tetrahedron (3-Simplex) at - // (1,1,1) - - // Determine which two tetrahedral vertices are the closest, out of - // (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). - byte aPoint = 0x06; - double aScore = xins; - byte bPoint = 0x05; - double bScore = yins; - if (aScore <= bScore && zins < bScore) { - bScore = zins; - bPoint = 0x03; - } - else if (aScore > bScore && zins < aScore) { - aScore = zins; - aPoint = 0x03; - } - - // Now we determine the two lattice points not part of the - // tetrahedron that may contribute. - // This depends on the closest two tetrahedral vertices, including - // (1,1,1) - double wins = 3 - inSum; - if (wins < aScore || wins < bScore) { // (1,1,1) is one of the - // closest two tetrahedral - // vertices. - byte c = (bScore < aScore ? bPoint : aPoint); // Our other - // closest - // vertex is the - // closest out - // of a and b. - - if ((c & 0x01) != 0) { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D; - dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; - } - else { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D; - } - - if ((c & 0x02) != 0) { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; - if ((c & 0x01) != 0) { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - else { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D; - } - - if ((c & 0x04) != 0) { - zsv_ext0 = zsb + 1; - zsv_ext1 = zsb + 2; - dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D; - } - else { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D; - } - } - else { // (1,1,1) is not one of the closest two tetrahedral - // vertices. - byte c = (byte) (aPoint & bPoint); // Our two extra vertices are - // determined by the closest - // two. - - if ((c & 0x01) != 0) { - xsv_ext0 = xsb + 1; - xsv_ext1 = xsb + 2; - dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; - dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; - } - else { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx0 - SQUISH_CONSTANT_3D; - dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; - } - - if ((c & 0x02) != 0) { - ysv_ext0 = ysb + 1; - ysv_ext1 = ysb + 2; - dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; - } - else { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy0 - SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; - } - - if ((c & 0x04) != 0) { - zsv_ext0 = zsb + 1; - zsv_ext1 = zsb + 2; - dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; - } - else { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz0 - SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; - } - } - - // Contribution (1,1,0) - double dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; - double dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; - double dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3); - } - - // Contribution (1,0,1) - double dx2 = dx3; - double dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; - double dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2); - } - - // Contribution (0,1,1) - double dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; - double dy1 = dy3; - double dz1 = dz2; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1); - } - - // Contribution (1,1,1) - dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; - dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; - dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; - if (attn0 > 0) { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0); - } - } - else { // We're inside the octahedron (Rectified 3-Simplex) in - // between. - double aScore; - byte aPoint; - boolean aIsFurtherSide; - double bScore; - byte bPoint; - boolean bIsFurtherSide; - - // Decide between point (0,0,1) and (1,1,0) as closest - double p1 = xins + yins; - if (p1 > 1) { - aScore = p1 - 1; - aPoint = 0x03; - aIsFurtherSide = true; - } - else { - aScore = 1 - p1; - aPoint = 0x04; - aIsFurtherSide = false; - } - - // Decide between point (0,1,0) and (1,0,1) as closest - double p2 = xins + zins; - if (p2 > 1) { - bScore = p2 - 1; - bPoint = 0x05; - bIsFurtherSide = true; - } - else { - bScore = 1 - p2; - bPoint = 0x02; - bIsFurtherSide = false; - } - - // The closest out of the two (1,0,0) and (0,1,1) will replace the - // furthest out of the two decided above, if closer. - double p3 = yins + zins; - if (p3 > 1) { - double score = p3 - 1; - if (aScore <= bScore && aScore < score) { - aScore = score; - aPoint = 0x06; - aIsFurtherSide = true; - } - else if (aScore > bScore && bScore < score) { - bScore = score; - bPoint = 0x06; - bIsFurtherSide = true; - } - } - else { - double score = 1 - p3; - if (aScore <= bScore && aScore < score) { - aScore = score; - aPoint = 0x01; - aIsFurtherSide = false; - } - else if (aScore > bScore && bScore < score) { - bScore = score; - bPoint = 0x01; - bIsFurtherSide = false; - } - } - - // Where each of the two closest points are determines how the extra - // two vertices are calculated. - if (aIsFurtherSide == bIsFurtherSide) { - if (aIsFurtherSide) { // Both closest points on (1,1,1) side - - // One of the two extra points is (1,1,1) - dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; - dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; - dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; - xsv_ext0 = xsb + 1; - ysv_ext0 = ysb + 1; - zsv_ext0 = zsb + 1; - - // Other extra point is based on the shared axis. - byte c = (byte) (aPoint & bPoint); - if ((c & 0x01) != 0) { - dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; - xsv_ext1 = xsb + 2; - ysv_ext1 = ysb; - zsv_ext1 = zsb; - } - else if ((c & 0x02) != 0) { - dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; - xsv_ext1 = xsb; - ysv_ext1 = ysb + 2; - zsv_ext1 = zsb; - } - else { - dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; - xsv_ext1 = xsb; - ysv_ext1 = ysb; - zsv_ext1 = zsb + 2; - } - } - else {// Both closest points on (0,0,0) side - - // One of the two extra points is (0,0,0) - dx_ext0 = dx0; - dy_ext0 = dy0; - dz_ext0 = dz0; - xsv_ext0 = xsb; - ysv_ext0 = ysb; - zsv_ext0 = zsb; - - // Other extra point is based on the omitted axis. - byte c = (byte) (aPoint | bPoint); - if ((c & 0x01) == 0) { - dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; - xsv_ext1 = xsb - 1; - ysv_ext1 = ysb + 1; - zsv_ext1 = zsb + 1; - } - else if ((c & 0x02) == 0) { - dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; - dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; - xsv_ext1 = xsb + 1; - ysv_ext1 = ysb - 1; - zsv_ext1 = zsb + 1; - } - else { - dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; - dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; - xsv_ext1 = xsb + 1; - ysv_ext1 = ysb + 1; - zsv_ext1 = zsb - 1; - } - } - } - else { // One point on (0,0,0) side, one point on (1,1,1) side - byte c1, c2; - if (aIsFurtherSide) { - c1 = aPoint; - c2 = bPoint; - } - else { - c1 = bPoint; - c2 = aPoint; - } - - // One contribution is a permutation of (1,1,-1) - if ((c1 & 0x01) == 0) { - dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D; - dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; - dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; - xsv_ext0 = xsb - 1; - ysv_ext0 = ysb + 1; - zsv_ext0 = zsb + 1; - } - else if ((c1 & 0x02) == 0) { - dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; - dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D; - dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; - xsv_ext0 = xsb + 1; - ysv_ext0 = ysb - 1; - zsv_ext0 = zsb + 1; - } - else { - dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; - dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; - dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D; - xsv_ext0 = xsb + 1; - ysv_ext0 = ysb + 1; - zsv_ext0 = zsb - 1; - } - - // One contribution is a permutation of (0,0,2) - dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; - dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; - dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; - xsv_ext1 = xsb; - ysv_ext1 = ysb; - zsv_ext1 = zsb; - if ((c2 & 0x01) != 0) { - dx_ext1 -= 2; - xsv_ext1 += 2; - } - else if ((c2 & 0x02) != 0) { - dy_ext1 -= 2; - ysv_ext1 += 2; - } - else { - dz_ext1 -= 2; - zsv_ext1 += 2; - } - } - - // Contribution (1,0,0) - double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; - double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; - double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); - } - - // Contribution (0,1,0) - double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; - double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; - double dz2 = dz1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); - } - - // Contribution (0,0,1) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); - } - - // Contribution (1,1,0) - double dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; - double dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; - double dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4; - if (attn4 > 0) { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4); - } - - // Contribution (1,0,1) - double dx5 = dx4; - double dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; - double dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; - double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5; - if (attn5 > 0) { - attn5 *= attn5; - value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5); - } - - // Contribution (0,1,1) - double dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; - double dy6 = dy4; - double dz6 = dz5; - double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6; - if (attn6 > 0) { - attn6 *= attn6; - value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6); - } - } - - // First extra vertex - double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0; - if (attn_ext0 > 0) { - attn_ext0 *= attn_ext0; - value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0); - } - - // Second extra vertex - double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1; - if (attn_ext1 > 0) { - attn_ext1 *= attn_ext1; - value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1); - } - - return value / NORM_CONSTANT_3D; - } - - // 4D OpenSimplex Noise. - public double eval(double x, double y, double z, double w) { - - // Place input coordinates on simplectic honeycomb. - double stretchOffset = (x + y + z + w) * STRETCH_CONSTANT_4D; - double xs = x + stretchOffset; - double ys = y + stretchOffset; - double zs = z + stretchOffset; - double ws = w + stretchOffset; - - // Floor to get simplectic honeycomb coordinates of rhombo-hypercube - // super-cell origin. - int xsb = fastFloor(xs); - int ysb = fastFloor(ys); - int zsb = fastFloor(zs); - int wsb = fastFloor(ws); - - // Skew out to get actual coordinates of stretched rhombo-hypercube - // origin. We'll need these later. - double squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_CONSTANT_4D; - double xb = xsb + squishOffset; - double yb = ysb + squishOffset; - double zb = zsb + squishOffset; - double wb = wsb + squishOffset; - - // Compute simplectic honeycomb coordinates relative to rhombo-hypercube - // origin. - double xins = xs - xsb; - double yins = ys - ysb; - double zins = zs - zsb; - double wins = ws - wsb; - - // Sum those together to get a value that determines which region we're - // in. - double inSum = xins + yins + zins + wins; - - // Positions relative to origin point. - double dx0 = x - xb; - double dy0 = y - yb; - double dz0 = z - zb; - double dw0 = w - wb; - - // We'll be defining these inside the next block and using them - // afterwards. - double dx_ext0, dy_ext0, dz_ext0, dw_ext0; - double dx_ext1, dy_ext1, dz_ext1, dw_ext1; - double dx_ext2, dy_ext2, dz_ext2, dw_ext2; - int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0; - int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1; - int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2; - - double value = 0; - if (inSum <= 1) { // We're inside the pentachoron (4-Simplex) at - // (0,0,0,0) - - // Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) - // are closest. - byte aPoint = 0x01; - double aScore = xins; - byte bPoint = 0x02; - double bScore = yins; - if (aScore >= bScore && zins > bScore) { - bScore = zins; - bPoint = 0x04; - } - else if (aScore < bScore && zins > aScore) { - aScore = zins; - aPoint = 0x04; - } - if (aScore >= bScore && wins > bScore) { - bScore = wins; - bPoint = 0x08; - } - else if (aScore < bScore && wins > aScore) { - aScore = wins; - aPoint = 0x08; - } - - // Now we determine the three lattice points not part of the - // pentachoron that may contribute. - // This depends on the closest two pentachoron vertices, including - // (0,0,0,0) - double uins = 1 - inSum; - if (uins > aScore || uins > bScore) { // (0,0,0,0) is one of the - // closest two pentachoron - // vertices. - byte c = (bScore > aScore ? bPoint : aPoint); // Our other - // closest - // vertex is the - // closest out - // of a and b. - if ((c & 0x01) == 0) { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsv_ext2 = xsb; - dx_ext0 = dx0 + 1; - dx_ext1 = dx_ext2 = dx0; - } - else { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; - dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1; - } - - if ((c & 0x02) == 0) { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy_ext1 = dy_ext2 = dy0; - if ((c & 0x01) == 0x01) { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - else { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1; - } - - if ((c & 0x04) == 0) { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz_ext1 = dz_ext2 = dz0; - if ((c & 0x03) != 0) { - if ((c & 0x03) == 0x03) { - zsv_ext0 -= 1; - dz_ext0 += 1; - } - else { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - } - else { - zsv_ext2 -= 1; - dz_ext2 += 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1; - } - - if ((c & 0x08) == 0) { - wsv_ext0 = wsv_ext1 = wsb; - wsv_ext2 = wsb - 1; - dw_ext0 = dw_ext1 = dw0; - dw_ext2 = dw0 + 1; - } - else { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; - dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1; - } - } - else { // (0,0,0,0) is not one of the closest two pentachoron - // vertices. - byte c = (byte) (aPoint | bPoint); // Our three extra vertices - // are determined by the - // closest two. - - if ((c & 0x01) == 0) { - xsv_ext0 = xsv_ext2 = xsb; - xsv_ext1 = xsb - 1; - dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; - dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_4D; - dx_ext2 = dx0 - SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; - dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - dx_ext1 = dx_ext2 = dx0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c & 0x02) == 0) { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; - dy_ext1 = dy_ext2 = dy0 - SQUISH_CONSTANT_4D; - if ((c & 0x01) == 0x01) { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - else { - ysv_ext2 -= 1; - dy_ext2 += 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - dy_ext1 = dy_ext2 = dy0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c & 0x04) == 0) { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; - dz_ext1 = dz_ext2 = dz0 - SQUISH_CONSTANT_4D; - if ((c & 0x03) == 0x03) { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - else { - zsv_ext2 -= 1; - dz_ext2 += 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - dz_ext1 = dz_ext2 = dz0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c & 0x08) == 0) { - wsv_ext0 = wsv_ext1 = wsb; - wsv_ext2 = wsb - 1; - dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 - SQUISH_CONSTANT_4D; - dw_ext2 = dw0 + 1 - SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; - dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - dw_ext1 = dw_ext2 = dw0 - 1 - SQUISH_CONSTANT_4D; - } - } - - // Contribution (0,0,0,0) - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; - if (attn0 > 0) { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0); - } - - // Contribution (1,0,0,0) - double dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; - double dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; - double dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; - double dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); - } - - // Contribution (0,1,0,0) - double dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; - double dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; - double dz2 = dz1; - double dw2 = dw1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); - } - - // Contribution (0,0,1,0) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; - double dw3 = dw1; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); - } - - // Contribution (0,0,0,1) - double dx4 = dx2; - double dy4 = dy1; - double dz4 = dz1; - double dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); - } - } - else if (inSum >= 3) { // We're inside the pentachoron (4-Simplex) at - // (1,1,1,1) - // Determine which two of (1,1,1,0), - // (1,1,0,1), (1,0,1,1), (0,1,1,1) - // are closest. - byte aPoint = 0x0E; - double aScore = xins; - byte bPoint = 0x0D; - double bScore = yins; - if (aScore <= bScore && zins < bScore) { - bScore = zins; - bPoint = 0x0B; - } - else if (aScore > bScore && zins < aScore) { - aScore = zins; - aPoint = 0x0B; - } - if (aScore <= bScore && wins < bScore) { - bScore = wins; - bPoint = 0x07; - } - else if (aScore > bScore && wins < aScore) { - aScore = wins; - aPoint = 0x07; - } - - // Now we determine the three lattice points not part of the - // pentachoron that may contribute. - // This depends on the closest two pentachoron vertices, including - // (0,0,0,0) - double uins = 4 - inSum; - if (uins < aScore || uins < bScore) { // (1,1,1,1) is one of the - // closest two pentachoron - // vertices. - byte c = (bScore < aScore ? bPoint : aPoint); // Our other - // closest - // vertex is the - // closest out - // of a and b. - - if ((c & 0x01) != 0) { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsv_ext2 = xsb + 1; - dx_ext0 = dx0 - 2 - 4 * SQUISH_CONSTANT_4D; - dx_ext1 = dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; - dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x02) != 0) { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; - if ((c & 0x01) != 0) { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - else { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x04) != 0) { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; - if ((c & 0x03) != 0x03) { - if ((c & 0x03) == 0) { - zsv_ext0 += 1; - dz_ext0 -= 1; - } - else { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else { - zsv_ext2 += 1; - dz_ext2 -= 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x08) != 0) { - wsv_ext0 = wsv_ext1 = wsb + 1; - wsv_ext2 = wsb + 2; - dw_ext0 = dw_ext1 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 2 - 4 * SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; - dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * SQUISH_CONSTANT_4D; - } - } - else { // (1,1,1,1) is not one of the closest two pentachoron - // vertices. - byte c = (byte) (aPoint & bPoint); // Our three extra vertices - // are determined by the - // closest two. - - if ((c & 0x01) != 0) { - xsv_ext0 = xsv_ext2 = xsb + 1; - xsv_ext1 = xsb + 2; - dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; - dx_ext2 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; - dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; - dx_ext1 = dx_ext2 = dx0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x02) != 0) { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - dy_ext1 = dy_ext2 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; - if ((c & 0x01) != 0) { - ysv_ext2 += 1; - dy_ext2 -= 1; - } - else { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; - dy_ext1 = dy_ext2 = dy0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x04) != 0) { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - dz_ext1 = dz_ext2 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; - if ((c & 0x03) != 0) { - zsv_ext2 += 1; - dz_ext2 -= 1; - } - else { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; - dz_ext1 = dz_ext2 = dz0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x08) != 0) { - wsv_ext0 = wsv_ext1 = wsb + 1; - wsv_ext2 = wsb + 2; - dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; - dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; - dw_ext1 = dw_ext2 = dw0 - 3 * SQUISH_CONSTANT_4D; - } - } - - // Contribution (1,1,1,0) - double dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; - double dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; - double dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; - double dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); - } - - // Contribution (1,1,0,1) - double dx3 = dx4; - double dy3 = dy4; - double dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; - double dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); - } - - // Contribution (1,0,1,1) - double dx2 = dx4; - double dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; - double dz2 = dz4; - double dw2 = dw3; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); - } - - // Contribution (0,1,1,1) - double dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; - double dz1 = dz4; - double dy1 = dy4; - double dw1 = dw3; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); - } - - // Contribution (1,1,1,1) - dx0 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; - dy0 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; - dz0 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; - dw0 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; - if (attn0 > 0) { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0); - } - } - else if (inSum <= 2) { // We're inside the first dispentachoron - // (Rectified 4-Simplex) - double aScore; - byte aPoint; - boolean aIsBiggerSide = true; - double bScore; - byte bPoint; - boolean bIsBiggerSide = true; - - // Decide between (1,1,0,0) and (0,0,1,1) - if (xins + yins > zins + wins) { - aScore = xins + yins; - aPoint = 0x03; - } - else { - aScore = zins + wins; - aPoint = 0x0C; - } - - // Decide between (1,0,1,0) and (0,1,0,1) - if (xins + zins > yins + wins) { - bScore = xins + zins; - bPoint = 0x05; - } - else { - bScore = yins + wins; - bPoint = 0x0A; - } - - // Closer between (1,0,0,1) and (0,1,1,0) will replace the further - // of a and b, if closer. - if (xins + wins > yins + zins) { - double score = xins + wins; - if (aScore >= bScore && score > bScore) { - bScore = score; - bPoint = 0x09; - } - else if (aScore < bScore && score > aScore) { - aScore = score; - aPoint = 0x09; - } - } - else { - double score = yins + zins; - if (aScore >= bScore && score > bScore) { - bScore = score; - bPoint = 0x06; - } - else if (aScore < bScore && score > aScore) { - aScore = score; - aPoint = 0x06; - } - } - - // Decide if (1,0,0,0) is closer. - double p1 = 2 - inSum + xins; - if (aScore >= bScore && p1 > bScore) { - bScore = p1; - bPoint = 0x01; - bIsBiggerSide = false; - } - else if (aScore < bScore && p1 > aScore) { - aScore = p1; - aPoint = 0x01; - aIsBiggerSide = false; - } - - // Decide if (0,1,0,0) is closer. - double p2 = 2 - inSum + yins; - if (aScore >= bScore && p2 > bScore) { - bScore = p2; - bPoint = 0x02; - bIsBiggerSide = false; - } - else if (aScore < bScore && p2 > aScore) { - aScore = p2; - aPoint = 0x02; - aIsBiggerSide = false; - } - - // Decide if (0,0,1,0) is closer. - double p3 = 2 - inSum + zins; - if (aScore >= bScore && p3 > bScore) { - bScore = p3; - bPoint = 0x04; - bIsBiggerSide = false; - } - else if (aScore < bScore && p3 > aScore) { - aScore = p3; - aPoint = 0x04; - aIsBiggerSide = false; - } - - // Decide if (0,0,0,1) is closer. - double p4 = 2 - inSum + wins; - if (aScore >= bScore && p4 > bScore) { - bScore = p4; - bPoint = 0x08; - bIsBiggerSide = false; - } - else if (aScore < bScore && p4 > aScore) { - aScore = p4; - aPoint = 0x08; - aIsBiggerSide = false; - } - - // Where each of the two closest points are determines how the extra - // three vertices are calculated. - if (aIsBiggerSide == bIsBiggerSide) { - if (aIsBiggerSide) { // Both closest points on the bigger side - byte c1 = (byte) (aPoint | bPoint); - byte c2 = (byte) (aPoint & bPoint); - if ((c1 & 0x01) == 0) { - xsv_ext0 = xsb; - xsv_ext1 = xsb - 1; - dx_ext0 = dx0 - 3 * SQUISH_CONSTANT_4D; - dx_ext1 = dx0 + 1 - 2 * SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x02) == 0) { - ysv_ext0 = ysb; - ysv_ext1 = ysb - 1; - dy_ext0 = dy0 - 3 * SQUISH_CONSTANT_4D; - dy_ext1 = dy0 + 1 - 2 * SQUISH_CONSTANT_4D; - } - else { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; - dy_ext1 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x04) == 0) { - zsv_ext0 = zsb; - zsv_ext1 = zsb - 1; - dz_ext0 = dz0 - 3 * SQUISH_CONSTANT_4D; - dz_ext1 = dz0 + 1 - 2 * SQUISH_CONSTANT_4D; - } - else { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; - dz_ext1 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x08) == 0) { - wsv_ext0 = wsb; - wsv_ext1 = wsb - 1; - dw_ext0 = dw0 - 3 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 + 1 - 2 * SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsb + 1; - dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - } - - // One combination is a permutation of (0,0,0,2) based on c2 - xsv_ext2 = xsb; - ysv_ext2 = ysb; - zsv_ext2 = zsb; - wsv_ext2 = wsb; - dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; - dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; - dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; - if ((c2 & 0x01) != 0) { - xsv_ext2 += 2; - dx_ext2 -= 2; - } - else if ((c2 & 0x02) != 0) { - ysv_ext2 += 2; - dy_ext2 -= 2; - } - else if ((c2 & 0x04) != 0) { - zsv_ext2 += 2; - dz_ext2 -= 2; - } - else { - wsv_ext2 += 2; - dw_ext2 -= 2; - } - - } - else { // Both closest points on the smaller side - // One of the two extra points is (0,0,0,0) - xsv_ext2 = xsb; - ysv_ext2 = ysb; - zsv_ext2 = zsb; - wsv_ext2 = wsb; - dx_ext2 = dx0; - dy_ext2 = dy0; - dz_ext2 = dz0; - dw_ext2 = dw0; - - // Other two points are based on the omitted axes. - byte c = (byte) (aPoint | bPoint); - - if ((c & 0x01) == 0) { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsb; - dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c & 0x02) == 0) { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; - if ((c & 0x01) == 0x01) { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - else { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c & 0x04) == 0) { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; - if ((c & 0x03) == 0x03) { - zsv_ext0 -= 1; - dz_ext0 += 1; - } - else { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c & 0x08) == 0) { - wsv_ext0 = wsb; - wsv_ext1 = wsb - 1; - dw_ext0 = dw0 - SQUISH_CONSTANT_4D; - dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsb + 1; - dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; - } - - } - } - else { // One point on each "side" - byte c1, c2; - if (aIsBiggerSide) { - c1 = aPoint; - c2 = bPoint; - } - else { - c1 = bPoint; - c2 = aPoint; - } - - // Two contributions are the bigger-sided point with each 0 - // replaced with -1. - if ((c1 & 0x01) == 0) { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsb; - dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x02) == 0) { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; - if ((c1 & 0x01) == 0x01) { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - else { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x04) == 0) { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; - if ((c1 & 0x03) == 0x03) { - zsv_ext0 -= 1; - dz_ext0 += 1; - } - else { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x08) == 0) { - wsv_ext0 = wsb; - wsv_ext1 = wsb - 1; - dw_ext0 = dw0 - SQUISH_CONSTANT_4D; - dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsb + 1; - dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; - } - - // One contribution is a permutation of (0,0,0,2) based on the - // smaller-sided point - xsv_ext2 = xsb; - ysv_ext2 = ysb; - zsv_ext2 = zsb; - wsv_ext2 = wsb; - dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; - dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; - dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; - if ((c2 & 0x01) != 0) { - xsv_ext2 += 2; - dx_ext2 -= 2; - } - else if ((c2 & 0x02) != 0) { - ysv_ext2 += 2; - dy_ext2 -= 2; - } - else if ((c2 & 0x04) != 0) { - zsv_ext2 += 2; - dz_ext2 -= 2; - } - else { - wsv_ext2 += 2; - dw_ext2 -= 2; - } - } - - // Contribution (1,0,0,0) - double dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; - double dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; - double dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; - double dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); - } - - // Contribution (0,1,0,0) - double dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; - double dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; - double dz2 = dz1; - double dw2 = dw1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); - } - - // Contribution (0,0,1,0) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; - double dw3 = dw1; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); - } - - // Contribution (0,0,0,1) - double dx4 = dx2; - double dy4 = dy1; - double dz4 = dz1; - double dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); - } - - // Contribution (1,1,0,0) - double dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; - double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; - if (attn5 > 0) { - attn5 *= attn5; - value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); - } - - // Contribution (1,0,1,0) - double dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; - double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; - if (attn6 > 0) { - attn6 *= attn6; - value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); - } - - // Contribution (1,0,0,1) - double dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; - if (attn7 > 0) { - attn7 *= attn7; - value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); - } - - // Contribution (0,1,1,0) - double dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; - double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; - if (attn8 > 0) { - attn8 *= attn8; - value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); - } - - // Contribution (0,1,0,1) - double dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; - if (attn9 > 0) { - attn9 *= attn9; - value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); - } - - // Contribution (0,0,1,1) - double dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; - if (attn10 > 0) { - attn10 *= attn10; - value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); - } - } - else { // We're inside the second dispentachoron (Rectified 4-Simplex) - double aScore; - byte aPoint; - boolean aIsBiggerSide = true; - double bScore; - byte bPoint; - boolean bIsBiggerSide = true; - - // Decide between (0,0,1,1) and (1,1,0,0) - if (xins + yins < zins + wins) { - aScore = xins + yins; - aPoint = 0x0C; - } - else { - aScore = zins + wins; - aPoint = 0x03; - } - - // Decide between (0,1,0,1) and (1,0,1,0) - if (xins + zins < yins + wins) { - bScore = xins + zins; - bPoint = 0x0A; - } - else { - bScore = yins + wins; - bPoint = 0x05; - } - - // Closer between (0,1,1,0) and (1,0,0,1) will replace the further - // of a and b, if closer. - if (xins + wins < yins + zins) { - double score = xins + wins; - if (aScore <= bScore && score < bScore) { - bScore = score; - bPoint = 0x06; - } - else if (aScore > bScore && score < aScore) { - aScore = score; - aPoint = 0x06; - } - } - else { - double score = yins + zins; - if (aScore <= bScore && score < bScore) { - bScore = score; - bPoint = 0x09; - } - else if (aScore > bScore && score < aScore) { - aScore = score; - aPoint = 0x09; - } - } - - // Decide if (0,1,1,1) is closer. - double p1 = 3 - inSum + xins; - if (aScore <= bScore && p1 < bScore) { - bScore = p1; - bPoint = 0x0E; - bIsBiggerSide = false; - } - else if (aScore > bScore && p1 < aScore) { - aScore = p1; - aPoint = 0x0E; - aIsBiggerSide = false; - } - - // Decide if (1,0,1,1) is closer. - double p2 = 3 - inSum + yins; - if (aScore <= bScore && p2 < bScore) { - bScore = p2; - bPoint = 0x0D; - bIsBiggerSide = false; - } - else if (aScore > bScore && p2 < aScore) { - aScore = p2; - aPoint = 0x0D; - aIsBiggerSide = false; - } - - // Decide if (1,1,0,1) is closer. - double p3 = 3 - inSum + zins; - if (aScore <= bScore && p3 < bScore) { - bScore = p3; - bPoint = 0x0B; - bIsBiggerSide = false; - } - else if (aScore > bScore && p3 < aScore) { - aScore = p3; - aPoint = 0x0B; - aIsBiggerSide = false; - } - - // Decide if (1,1,1,0) is closer. - double p4 = 3 - inSum + wins; - if (aScore <= bScore && p4 < bScore) { - bScore = p4; - bPoint = 0x07; - bIsBiggerSide = false; - } - else if (aScore > bScore && p4 < aScore) { - aScore = p4; - aPoint = 0x07; - aIsBiggerSide = false; - } - - // Where each of the two closest points are determines how the extra - // three vertices are calculated. - if (aIsBiggerSide == bIsBiggerSide) { - if (aIsBiggerSide) { // Both closest points on the bigger side - byte c1 = (byte) (aPoint & bPoint); - byte c2 = (byte) (aPoint | bPoint); - - // Two contributions are permutations of (0,0,0,1) and - // (0,0,0,2) based on c1 - xsv_ext0 = xsv_ext1 = xsb; - ysv_ext0 = ysv_ext1 = ysb; - zsv_ext0 = zsv_ext1 = zsb; - wsv_ext0 = wsv_ext1 = wsb; - dx_ext0 = dx0 - SQUISH_CONSTANT_4D; - dy_ext0 = dy0 - SQUISH_CONSTANT_4D; - dz_ext0 = dz0 - SQUISH_CONSTANT_4D; - dw_ext0 = dw0 - SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_4D; - dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_4D; - dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 - 2 * SQUISH_CONSTANT_4D; - if ((c1 & 0x01) != 0) { - xsv_ext0 += 1; - dx_ext0 -= 1; - xsv_ext1 += 2; - dx_ext1 -= 2; - } - else if ((c1 & 0x02) != 0) { - ysv_ext0 += 1; - dy_ext0 -= 1; - ysv_ext1 += 2; - dy_ext1 -= 2; - } - else if ((c1 & 0x04) != 0) { - zsv_ext0 += 1; - dz_ext0 -= 1; - zsv_ext1 += 2; - dz_ext1 -= 2; - } - else { - wsv_ext0 += 1; - dw_ext0 -= 1; - wsv_ext1 += 2; - dw_ext1 -= 2; - } - - // One contribution is a permutation of (1,1,1,-1) based on - // c2 - xsv_ext2 = xsb + 1; - ysv_ext2 = ysb + 1; - zsv_ext2 = zsb + 1; - wsv_ext2 = wsb + 1; - dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - if ((c2 & 0x01) == 0) { - xsv_ext2 -= 2; - dx_ext2 += 2; - } - else if ((c2 & 0x02) == 0) { - ysv_ext2 -= 2; - dy_ext2 += 2; - } - else if ((c2 & 0x04) == 0) { - zsv_ext2 -= 2; - dz_ext2 += 2; - } - else { - wsv_ext2 -= 2; - dw_ext2 += 2; - } - } - else { // Both closest points on the smaller side - // One of the two extra points is (1,1,1,1) - xsv_ext2 = xsb + 1; - ysv_ext2 = ysb + 1; - zsv_ext2 = zsb + 1; - wsv_ext2 = wsb + 1; - dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; - dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; - dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; - - // Other two points are based on the shared axes. - byte c = (byte) (aPoint & bPoint); - - if ((c & 0x01) != 0) { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x02) != 0) { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; - if ((c & 0x01) == 0) { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - else { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x04) != 0) { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; - if ((c & 0x03) == 0) { - zsv_ext0 += 1; - dz_ext0 -= 1; - } - else { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c & 0x08) != 0) { - wsv_ext0 = wsb + 1; - wsv_ext1 = wsb + 2; - dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsb; - dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; - } - } - } - else { // One point on each "side" - byte c1, c2; - if (aIsBiggerSide) { - c1 = aPoint; - c2 = bPoint; - } - else { - c1 = bPoint; - c2 = aPoint; - } - - // Two contributions are the bigger-sided point with each 1 - // replaced with 2. - if ((c1 & 0x01) != 0) { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; - dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; - } - else { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x02) != 0) { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; - if ((c1 & 0x01) == 0) { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - else { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - } - else { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x04) != 0) { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; - if ((c1 & 0x03) == 0) { - zsv_ext0 += 1; - dz_ext0 -= 1; - } - else { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; - } - - if ((c1 & 0x08) != 0) { - wsv_ext0 = wsb + 1; - wsv_ext1 = wsb + 2; - dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; - dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; - } - else { - wsv_ext0 = wsv_ext1 = wsb; - dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; - } - - // One contribution is a permutation of (1,1,1,-1) based on the - // smaller-sided point - xsv_ext2 = xsb + 1; - ysv_ext2 = ysb + 1; - zsv_ext2 = zsb + 1; - wsv_ext2 = wsb + 1; - dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - if ((c2 & 0x01) == 0) { - xsv_ext2 -= 2; - dx_ext2 += 2; - } - else if ((c2 & 0x02) == 0) { - ysv_ext2 -= 2; - dy_ext2 += 2; - } - else if ((c2 & 0x04) == 0) { - zsv_ext2 -= 2; - dz_ext2 += 2; - } - else { - wsv_ext2 -= 2; - dw_ext2 += 2; - } - } - - // Contribution (1,1,1,0) - double dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; - double dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; - double dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; - double dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); - } - - // Contribution (1,1,0,1) - double dx3 = dx4; - double dy3 = dy4; - double dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; - double dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); - } - - // Contribution (1,0,1,1) - double dx2 = dx4; - double dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; - double dz2 = dz4; - double dw2 = dw3; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); - } - - // Contribution (0,1,1,1) - double dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; - double dz1 = dz4; - double dy1 = dy4; - double dw1 = dw3; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); - } - - // Contribution (1,1,0,0) - double dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; - double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; - if (attn5 > 0) { - attn5 *= attn5; - value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); - } - - // Contribution (1,0,1,0) - double dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; - double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; - if (attn6 > 0) { - attn6 *= attn6; - value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); - } - - // Contribution (1,0,0,1) - double dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; - if (attn7 > 0) { - attn7 *= attn7; - value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); - } - - // Contribution (0,1,1,0) - double dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; - double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; - if (attn8 > 0) { - attn8 *= attn8; - value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); - } - - // Contribution (0,1,0,1) - double dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; - if (attn9 > 0) { - attn9 *= attn9; - value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); - } - - // Contribution (0,0,1,1) - double dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; - double dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; - double dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; - double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; - if (attn10 > 0) { - attn10 *= attn10; - value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); - } - } - - // First extra vertex - double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0; - if (attn_ext0 > 0) { - attn_ext0 *= attn_ext0; - value += attn_ext0 * attn_ext0 * extrapolate( - xsv_ext0, - ysv_ext0, - zsv_ext0, - wsv_ext0, - dx_ext0, - dy_ext0, - dz_ext0, - dw_ext0 - ); - } - - // Second extra vertex - double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1; - if (attn_ext1 > 0) { - attn_ext1 *= attn_ext1; - value += attn_ext1 * attn_ext1 * extrapolate( - xsv_ext1, - ysv_ext1, - zsv_ext1, - wsv_ext1, - dx_ext1, - dy_ext1, - dz_ext1, - dw_ext1 - ); - } - - // Third extra vertex - double attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2; - if (attn_ext2 > 0) { - attn_ext2 *= attn_ext2; - value += attn_ext2 * attn_ext2 * extrapolate( - xsv_ext2, - ysv_ext2, - zsv_ext2, - wsv_ext2, - dx_ext2, - dy_ext2, - dz_ext2, - dw_ext2 - ); - } - - return value / NORM_CONSTANT_4D; - } - - private double extrapolate(int xsb, int ysb, double dx, double dy) { - int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E; - return gradients2D[index] * dx + gradients2D[index + 1] * dy; - } - - private double extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) { - int index = permGradIndex3D[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF]; - return gradients3D[index] * dx + gradients3D[index + 1] * dy + gradients3D[index + 2] * dz; - } - - private double extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) { - int index = perm[(perm[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC; - return gradients4D[index] * dx + gradients4D[index + 1] * dy + gradients4D[index + 2] * dz + gradients4D[index + 3] * dw; - } - - private static int fastFloor(double x) { - int xi = (int) x; - return x < xi ? xi - 1 : xi; - } - - // Gradients for 2D. They approximate the directions to the - // vertices of an octagon from the center. - private static byte[] gradients2D = new byte[] {5, 2, 2, 5, -5, 2, -2, 5, 5, -2, 2, -5, -5, -2, -2, -5,}; - - // Gradients for 3D. They approximate the directions to the - // vertices of a rhombicuboctahedron from the center, skewed so - // that the triangular and square facets can be inscribed inside - // circles of the same radius. - private static byte[] gradients3D = new byte[] { - -11, - 4, - 4, - -4, - 11, - 4, - -4, - 4, - 11, - 11, - 4, - 4, - 4, - 11, - 4, - 4, - 4, - 11, - -11, - -4, - 4, - -4, - -11, - 4, - -4, - -4, - 11, - 11, - -4, - 4, - 4, - -11, - 4, - 4, - -4, - 11, - -11, - 4, - -4, - -4, - 11, - -4, - -4, - 4, - -11, - 11, - 4, - -4, - 4, - 11, - -4, - 4, - 4, - -11, - -11, - -4, - -4, - -4, - -11, - -4, - -4, - -4, - -11, - 11, - -4, - -4, - 4, - -11, - -4, - 4, - -4, - -11, - }; - - // Gradients for 4D. They approximate the directions to the - // vertices of a disprismatotesseractihexadecachoron from the center, - // skewed so that the tetrahedral and cubic facets can be inscribed inside - // spheres of the same radius. - private static byte[] gradients4D = new byte[] { - 3, - 1, - 1, - 1, - 1, - 3, - 1, - 1, - 1, - 1, - 3, - 1, - 1, - 1, - 1, - 3, - -3, - 1, - 1, - 1, - -1, - 3, - 1, - 1, - -1, - 1, - 3, - 1, - -1, - 1, - 1, - 3, - 3, - -1, - 1, - 1, - 1, - -3, - 1, - 1, - 1, - -1, - 3, - 1, - 1, - -1, - 1, - 3, - -3, - -1, - 1, - 1, - -1, - -3, - 1, - 1, - -1, - -1, - 3, - 1, - -1, - -1, - 1, - 3, - 3, - 1, - -1, - 1, - 1, - 3, - -1, - 1, - 1, - 1, - -3, - 1, - 1, - 1, - -1, - 3, - -3, - 1, - -1, - 1, - -1, - 3, - -1, - 1, - -1, - 1, - -3, - 1, - -1, - 1, - -1, - 3, - 3, - -1, - -1, - 1, - 1, - -3, - -1, - 1, - 1, - -1, - -3, - 1, - 1, - -1, - -1, - 3, - -3, - -1, - -1, - 1, - -1, - -3, - -1, - 1, - -1, - -1, - -3, - 1, - -1, - -1, - -1, - 3, - 3, - 1, - 1, - -1, - 1, - 3, - 1, - -1, - 1, - 1, - 3, - -1, - 1, - 1, - 1, - -3, - -3, - 1, - 1, - -1, - -1, - 3, - 1, - -1, - -1, - 1, - 3, - -1, - -1, - 1, - 1, - -3, - 3, - -1, - 1, - -1, - 1, - -3, - 1, - -1, - 1, - -1, - 3, - -1, - 1, - -1, - 1, - -3, - -3, - -1, - 1, - -1, - -1, - -3, - 1, - -1, - -1, - -1, - 3, - -1, - -1, - -1, - 1, - -3, - 3, - 1, - -1, - -1, - 1, - 3, - -1, - -1, - 1, - 1, - -3, - -1, - 1, - 1, - -1, - -3, - -3, - 1, - -1, - -1, - -1, - 3, - -1, - -1, - -1, - 1, - -3, - -1, - -1, - 1, - -1, - -3, - 3, - -1, - -1, - -1, - 1, - -3, - -1, - -1, - 1, - -1, - -3, - -1, - 1, - -1, - -1, - -3, - -3, - -1, - -1, - -1, - -1, - -3, - -1, - -1, - -1, - -1, - -3, - -1, - -1, - -1, - -1, - -3, - }; -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/noise/VoronoiNoise.java b/src/main/java/ru/bclib/noise/VoronoiNoise.java deleted file mode 100644 index f1e8a033..00000000 --- a/src/main/java/ru/bclib/noise/VoronoiNoise.java +++ /dev/null @@ -1,148 +0,0 @@ -package ru.bclib.noise; - -import net.minecraft.core.BlockPos; -import ru.bclib.util.MHelper; - -import java.util.Random;import net.minecraft.util.RandomSource; - -public class VoronoiNoise { - private static final Random RANDOM = new Random(); - final int seed; - - public VoronoiNoise() { - this(0); - } - - public VoronoiNoise(int seed) { - this.seed = seed; - } - - private int getSeed(int x, int y, int z) { - int h = seed + x * 374761393 + y * 668265263 + z; - h = (h ^ (h >> 13)) * 1274126177; - return h ^ (h >> 16); - } - - public double sample(double x, double y, double z) { - int ix = MHelper.floor(x); - int iy = MHelper.floor(y); - int iz = MHelper.floor(z); - - float px = (float) (x - ix); - float py = (float) (y - iy); - float pz = (float) (z - iz); - - float d = 10; - - for (int pox = -1; pox < 2; pox++) { - for (int poy = -1; poy < 2; poy++) { - for (int poz = -1; poz < 2; poz++) { - RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); - float pointX = pox + RANDOM.nextFloat(); - float pointY = poy + RANDOM.nextFloat(); - float pointZ = poz + RANDOM.nextFloat(); - float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); - if (d2 < d) { - d = d2; - } - } - } - } - - return Math.sqrt(d); - } - - public Random getRandom(double x, double y, double z) { - int ix = MHelper.floor(x); - int iy = MHelper.floor(y); - int iz = MHelper.floor(z); - - float px = (float) (x - ix); - float py = (float) (y - iy); - float pz = (float) (z - iz); - - float d = 10; - - int posX = 0; - int posY = 0; - int posZ = 0; - - for (int pox = -1; pox < 2; pox++) { - for (int poy = -1; poy < 2; poy++) { - for (int poz = -1; poz < 2; poz++) { - RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); - float pointX = pox + RANDOM.nextFloat(); - float pointY = poy + RANDOM.nextFloat(); - float pointZ = poz + RANDOM.nextFloat(); - float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); - if (d2 < d) { - d = d2; - posX = pox; - posY = poy; - posZ = poz; - } - } - } - } - - posX += ix; - posY += iy; - posZ += iz; - - int seed = MHelper.getSeed(posY, posX, posZ); - RANDOM.setSeed(seed); - - return RANDOM; - } - - public BlockPos[] getPos(double x, double y, double z, double scale) { - int ix = MHelper.floor(x); - int iy = MHelper.floor(y); - int iz = MHelper.floor(z); - - float px = (float) (x - ix); - float py = (float) (y - iy); - float pz = (float) (z - iz); - - float d = 10; - float selX = 0; - float selY = 0; - float selZ = 0; - float selXPre = 0; - float selYPre = 0; - float selZPre = 0; - - for (int pox = -1; pox < 2; pox++) { - for (int poy = -1; poy < 2; poy++) { - for (int poz = -1; poz < 2; poz++) { - RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); - float pointX = pox + RANDOM.nextFloat(); - float pointY = poy + RANDOM.nextFloat(); - float pointZ = poz + RANDOM.nextFloat(); - float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); - if (d2 < d) { - d = d2; - selXPre = selX; - selYPre = selY; - selZPre = selZ; - selX = pointX; - selY = pointY; - selZ = pointZ; - } - } - } - } - - BlockPos p1 = new BlockPos( - (ix + (double) selX) * scale, - (iy + (double) selY) * scale, - (iz + (double) selZ) * scale - ); - BlockPos p2 = new BlockPos( - (ix + (double) selXPre) * scale, - (iy + (double) selYPre) * scale, - (iz + (double) selZPre) * scale - ); - return new BlockPos[] {p1, p2}; - } -} diff --git a/src/main/java/ru/bclib/recipes/AnvilRecipe.java b/src/main/java/ru/bclib/recipes/AnvilRecipe.java deleted file mode 100644 index 403c739f..00000000 --- a/src/main/java/ru/bclib/recipes/AnvilRecipe.java +++ /dev/null @@ -1,338 +0,0 @@ -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.core.Registry; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -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.tag.CommonItemTags; -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, UnknownReceipBookCategory { - public final static String GROUP = "smithing"; - public final static RecipeType 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); - - public static void register(){ - - } - - 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); - //TODO: 1.18.2 Test if hammer still works - if (hammer.isEmpty() || !hammer.is(CommonItemTags.HAMMERS)) { - 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 getIngredients() { - ; - NonNullList defaultedList = NonNullList.create(); - defaultedList.add(Ingredient.of(Registry.ITEM.stream() - .filter(item->item.builtInRegistryHolder().is(CommonItemTags.HAMMERS)) - .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(TagKey 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) { - return; - } - - 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 { - @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); - } - } -} diff --git a/src/main/java/ru/bclib/recipes/BCLRecipeManager.java b/src/main/java/ru/bclib/recipes/BCLRecipeManager.java deleted file mode 100644 index 64473e0e..00000000 --- a/src/main/java/ru/bclib/recipes/BCLRecipeManager.java +++ /dev/null @@ -1,112 +0,0 @@ -package ru.bclib.recipes; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.Container; -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 net.minecraft.world.level.block.Block; -import ru.bclib.util.CollectionsUtil; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.function.Function; - -public class BCLRecipeManager { - private static final Map, Map>> RECIPES = Maps.newHashMap(); - private static final Map, Object> SORTED = Maps.newHashMap(); - private static final String MINECRAFT = "minecraft"; - - public static > Optional getSortedRecipe(RecipeType type, C inventory, Level level, Function, Map>> getter) { - List> recipes = (List>) SORTED.computeIfAbsent(type, t -> { - Collection> values = getter.apply(type).values(); - List> list = new ArrayList<>(values); - list.sort((v1, v2) -> { - boolean b1 = v1.getId().getNamespace().equals(MINECRAFT); - boolean b2 = v2.getId().getNamespace().equals(MINECRAFT); - return b1 ^ b2 ? (b1 ? 1 : -1) : 0; - }); - return ImmutableList.copyOf(list); - }); - return (Optional)recipes.stream().filter(recipe -> recipe.matches(inventory, level)).findFirst(); - } - - public static void addRecipe(RecipeType type, Recipe recipe) { - Map> list = RECIPES.computeIfAbsent(type, i -> Maps.newHashMap()); - list.put(recipe.getId(), recipe); - } - - public static > T getRecipe(RecipeType type, ResourceLocation id) { - Map> map = RECIPES.get(type); - return map != null ? (T) map.get(id) : null; - } - - public static Map, Map>> getMap(Map, Map>> recipes) { - Map, Map>> result = Maps.newHashMap(); - - for (RecipeType type : recipes.keySet()) { - Map> typeList = Maps.newHashMap(); - typeList.putAll(recipes.get(type)); - result.put(type, typeList); - } - - SORTED.clear(); - RECIPES.forEach((type, list) -> { - if (list != null) { - Map> typeList = result.computeIfAbsent(type, i -> Maps.newHashMap()); - for (Entry> entry : list.entrySet()) { - ResourceLocation id = entry.getKey(); - typeList.computeIfAbsent(id, i -> entry.getValue()); - } - } - }); - - return result; - } - - public static Map> getMapByName(Map> recipes) { - Map> result = CollectionsUtil.getMutable(recipes); - RECIPES.values().forEach(map -> map.forEach((location, recipe) -> result.computeIfAbsent(location, i -> recipe))); - return result; - } - - public static , T extends Recipe> S registerSerializer(String modID, String id, S serializer) { - return Registry.register(Registry.RECIPE_SERIALIZER, modID + ":" + id, serializer); - } - - public static > RecipeType registerType(String modID, String type) { - ResourceLocation recipeTypeId = new ResourceLocation(modID, type); - return Registry.register(Registry.RECIPE_TYPE, recipeTypeId, new RecipeType() { - public String toString() { - return type; - } - }); - } - - 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; - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/recipes/CraftingRecipes.java b/src/main/java/ru/bclib/recipes/CraftingRecipes.java deleted file mode 100644 index adfb59ae..00000000 --- a/src/main/java/ru/bclib/recipes/CraftingRecipes.java +++ /dev/null @@ -1,87 +0,0 @@ -package ru.bclib.recipes; - -import net.minecraft.tags.ItemTags; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.Blocks; -import ru.bclib.BCLib; -import ru.bclib.api.tag.CommonItemTags; -import ru.bclib.config.Configs; - -public class CraftingRecipes { - public static void init() { - GridRecipe.make(BCLib.MOD_ID, "tag_smith_table", Blocks.SMITHING_TABLE) - .setShape("II", "##", "##") - .addMaterial('#', ItemTags.PLANKS) - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_cauldron", Blocks.CAULDRON) - .setShape("I I", "I I", "III") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_hopper", Blocks.HOPPER) - .setShape("I I", "ICI", " I ") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('C', CommonItemTags.CHEST) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_piston", Blocks.PISTON) - .setShape("WWW", "CIC", "CDC") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('D', Items.REDSTONE) - .addMaterial('C', Items.COBBLESTONE) - .addMaterial('W', ItemTags.PLANKS) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_rail", Blocks.RAIL) - .setOutputCount(16) - .setShape("I I", "ISI", "I I") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('S', Items.STICK) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_stonecutter", Blocks.STONECUTTER) - .setShape(" I ", "SSS") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('S', Items.STONE) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_bucket", Items.BUCKET) - .setShape("I I", " I ") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_compass", Items.COMPASS) - .setShape(" I ", "IDI", " I ") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('D', Items.REDSTONE) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_minecart", Items.MINECART) - .setShape("I I", "III") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - GridRecipe.make(BCLib.MOD_ID, "tag_shield", Items.SHIELD) - .setShape("WIW", "WWW", " W ") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('W', ItemTags.PLANKS) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - - GridRecipe.make(BCLib.MOD_ID, "tag_hopper", Blocks.HOPPER) - .setShape("I I", "ICI", " I ") - .addMaterial('I', CommonItemTags.IRON_INGOTS) - .addMaterial('C', CommonItemTags.CHEST) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - - GridRecipe.make(BCLib.MOD_ID, "tag_shulker_box", Blocks.SHULKER_BOX) - .setShape("S", "C", "S") - .addMaterial('S', Items.SHULKER_SHELL) - .addMaterial('C', CommonItemTags.CHEST) - .checkConfig(Configs.RECIPE_CONFIG) - .build(); - } -} diff --git a/src/main/java/ru/bclib/recipes/FurnaceRecipe.java b/src/main/java/ru/bclib/recipes/FurnaceRecipe.java deleted file mode 100644 index 8fd556e1..00000000 --- a/src/main/java/ru/bclib/recipes/FurnaceRecipe.java +++ /dev/null @@ -1,128 +0,0 @@ -package ru.bclib.recipes; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.BlastingRecipe; -import net.minecraft.world.item.crafting.CampfireCookingRecipe; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.SmeltingRecipe; -import net.minecraft.world.item.crafting.SmokingRecipe; -import net.minecraft.world.level.ItemLike; -import ru.bclib.config.PathConfig; - -public class FurnaceRecipe { - private static final FurnaceRecipe INSTANCE = new FurnaceRecipe(); - - private ResourceLocation id; - private ItemLike input; - private ItemLike output; - private boolean exist; - private String group; - private int count; - private int time; - private float xp; - - private FurnaceRecipe() {} - - public static FurnaceRecipe make(String modID, String name, ItemLike input, ItemLike output) { - INSTANCE.id = new ResourceLocation(modID, name); - INSTANCE.group = ""; - INSTANCE.input = input; - INSTANCE.output = output; - INSTANCE.count = 1; - INSTANCE.time = 200; - INSTANCE.xp = 0; - INSTANCE.exist = BCLRecipeManager.exists(output) && BCLRecipeManager.exists(input); - return INSTANCE; - } - - public FurnaceRecipe checkConfig(PathConfig config) { - exist &= config.getBoolean("furnace", id.getPath(), true); - return this; - } - - public FurnaceRecipe setGroup(String group) { - this.group = group; - return this; - } - - public FurnaceRecipe setOutputCount(int count) { - this.count = count; - return this; - } - - public FurnaceRecipe setXP(float xp) { - this.xp = xp; - return this; - } - - public FurnaceRecipe setCookTime(int time) { - this.time = time; - return this; - } - - public void build() { - build(false, false, false); - } - - public void buildWithBlasting() { - build(true, false, false); - } - - public void buildFoodlike() { - build(false, true, true); - } - - public void build(boolean blasting, boolean campfire, boolean smoker) { - if (!exist) { - return; - } - - SmeltingRecipe recipe = new SmeltingRecipe( - new ResourceLocation(id + "_smelting"), - group, - Ingredient.of(input), - new ItemStack(output, count), - xp, - time - ); - BCLRecipeManager.addRecipe(RecipeType.SMELTING, recipe); - - if (blasting) { - BlastingRecipe recipe2 = new BlastingRecipe( - new ResourceLocation(id + "_blasting"), - group, - Ingredient.of(input), - new ItemStack(output, count), - xp, - time / 2 - ); - BCLRecipeManager.addRecipe(RecipeType.BLASTING, recipe2); - } - - if (campfire) { - CampfireCookingRecipe recipe2 = new CampfireCookingRecipe( - new ResourceLocation(id + "_campfire"), - group, - Ingredient.of(input), - new ItemStack(output, count), - xp, - time * 3 - ); - BCLRecipeManager.addRecipe(RecipeType.CAMPFIRE_COOKING, recipe2); - } - - if (smoker) { - SmokingRecipe recipe2 = new SmokingRecipe( - new ResourceLocation(id + "_smoker"), - group, - Ingredient.of(input), - new ItemStack(output, count), - xp, - time / 2 - ); - BCLRecipeManager.addRecipe(RecipeType.SMOKING, recipe2); - } - } -} diff --git a/src/main/java/ru/bclib/recipes/GridRecipe.java b/src/main/java/ru/bclib/recipes/GridRecipe.java deleted file mode 100644 index 1c56e7ed..00000000 --- a/src/main/java/ru/bclib/recipes/GridRecipe.java +++ /dev/null @@ -1,135 +0,0 @@ -package ru.bclib.recipes; - -import com.google.common.collect.Maps; -import net.minecraft.core.NonNullList; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.CraftingRecipe; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.ShapedRecipe; -import net.minecraft.world.item.crafting.ShapelessRecipe; -import net.minecraft.world.level.ItemLike; -import ru.bclib.config.PathConfig; - -import java.util.Arrays; -import java.util.Map; - -public class GridRecipe { - private static final GridRecipe INSTANCE = new GridRecipe(); - - private ResourceLocation id; - private ItemLike output; - - private String group; - private RecipeType type; - private boolean shaped; - private String[] shape; - private Map materialKeys = Maps.newHashMap(); - private int count; - private boolean exist; - - private GridRecipe() {} - - public static GridRecipe make(String modID, String name, ItemLike output) { - return make(new ResourceLocation(modID, name), output); - } - - public static GridRecipe make(ResourceLocation id, ItemLike output) { - INSTANCE.id = id; - INSTANCE.output = output; - - INSTANCE.group = ""; - INSTANCE.type = RecipeType.CRAFTING; - INSTANCE.shaped = true; - INSTANCE.shape = new String[] {"#"}; - INSTANCE.materialKeys.clear(); - INSTANCE.count = 1; - - INSTANCE.exist = output != null && BCLRecipeManager.exists(output); - - return INSTANCE; - } - - public GridRecipe checkConfig(PathConfig config) { - exist &= config.getBoolean("grid", id.getPath(), true); - return this; - } - - public GridRecipe setGroup(String group) { - this.group = group; - return this; - } - - public GridRecipe setShape(String... shape) { - this.shape = shape; - return this; - } - - public GridRecipe setList(String shape) { - this.shape = new String[] {shape}; - this.shaped = false; - return this; - } - - public GridRecipe addMaterial(char key, TagKey value) { - return addMaterial(key, Ingredient.of(value)); - } - - public GridRecipe addMaterial(char key, ItemStack... value) { - return addMaterial(key, Ingredient.of(Arrays.stream(value))); - } - - public GridRecipe addMaterial(char key, ItemLike... values) { - for (ItemLike item : values) { - exist &= BCLRecipeManager.exists(item); - } - return addMaterial(key, Ingredient.of(values)); - } - - private GridRecipe addMaterial(char key, Ingredient value) { - materialKeys.put(key, value); - return this; - } - - public GridRecipe setOutputCount(int count) { - this.count = count; - return this; - } - - private NonNullList getMaterials(int width, int height) { - NonNullList materials = NonNullList.withSize(width * height, Ingredient.EMPTY); - int pos = 0; - for (String line : shape) { - for (int i = 0; i < width; i++) { - char c = line.charAt(i); - Ingredient material = materialKeys.get(c); - materials.set(pos++, material == null ? Ingredient.EMPTY : material); - } - } - return materials; - } - - public void build() { - if (!exist) { - return; - } - - int height = shape.length; - int width = shape[0].length(); - ItemStack result = new ItemStack(output, count); - NonNullList materials = this.getMaterials(width, height); - - CraftingRecipe recipe = shaped ? new ShapedRecipe( - id, - group, - width, - height, - materials, - result - ) : new ShapelessRecipe(id, group, result, materials); - BCLRecipeManager.addRecipe(type, recipe); - } -} diff --git a/src/main/java/ru/bclib/recipes/SmithingTableRecipe.java b/src/main/java/ru/bclib/recipes/SmithingTableRecipe.java deleted file mode 100644 index ca86fbee..00000000 --- a/src/main/java/ru/bclib/recipes/SmithingTableRecipe.java +++ /dev/null @@ -1,102 +0,0 @@ -package ru.bclib.recipes; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.UpgradeRecipe; -import net.minecraft.world.level.ItemLike; -import ru.bclib.BCLib; -import ru.bclib.config.PathConfig; - -public class SmithingTableRecipe { - - private final static SmithingTableRecipe BUILDER = new SmithingTableRecipe(); - private final static RecipeType TYPE = RecipeType.SMITHING; - - public static SmithingTableRecipe create(String modID, String name) { - return create(new ResourceLocation(modID, name)); - } - - public static SmithingTableRecipe create(ResourceLocation id) { - BUILDER.id = id; - BUILDER.base = null; - BUILDER.addition = null; - BUILDER.result = null; - BUILDER.exist = true; - - return BUILDER; - } - - private ResourceLocation id; - private Ingredient base; - private Ingredient addition; - private ItemStack result; - private boolean exist; - - private SmithingTableRecipe() {} - - public SmithingTableRecipe checkConfig(PathConfig config) { - exist &= config.getBoolean("smithing", id.getPath(), true); - return this; - } - - public SmithingTableRecipe setResult(ItemLike item) { - return this.setResult(item, 1); - } - - public SmithingTableRecipe setResult(ItemLike item, int count) { - this.exist &= BCLRecipeManager.exists(item); - this.result = new ItemStack(item, count); - return this; - } - - public SmithingTableRecipe setBase(ItemLike... items) { - this.exist &= BCLRecipeManager.exists(items); - this.base = Ingredient.of(items); - return this; - } - - public SmithingTableRecipe setBase(TagKey tag) { - this.base = (Ingredient.of(tag)); - return this; - } - - public SmithingTableRecipe setAddition(ItemLike... items) { - this.exist &= BCLRecipeManager.exists(items); - this.addition = Ingredient.of(items); - return this; - } - - public SmithingTableRecipe setAddition(TagKey tag) { - this.addition = (Ingredient.of(tag)); - return this; - } - - public void build() { - if (!exist) { - return; - } - - if (base == null) { - BCLib.LOGGER.warning("Base input for Smithing recipe can't be 'null', recipe {} will be ignored!", id); - return; - } - if (addition == null) { - BCLib.LOGGER.warning("Addition input for Smithing recipe can't be 'null', recipe {} will be ignored!", id); - return; - } - if (result == null) { - BCLib.LOGGER.warning("Result for Smithing recipe can't be 'null', recipe {} will be ignored!", id); - return; - } - if (BCLRecipeManager.getRecipe(TYPE, id) != null) { - BCLib.LOGGER.warning("Can't add Smithing recipe! Id {} already exists!", id); - return; - } - - BCLRecipeManager.addRecipe(TYPE, new UpgradeRecipe(id, base, addition, result)); - } -} diff --git a/src/main/java/ru/bclib/registry/BaseBlockEntities.java b/src/main/java/ru/bclib/registry/BaseBlockEntities.java deleted file mode 100644 index 4c90e226..00000000 --- a/src/main/java/ru/bclib/registry/BaseBlockEntities.java +++ /dev/null @@ -1,58 +0,0 @@ -package ru.bclib.registry; - -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import ru.bclib.BCLib; -import ru.bclib.blockentities.BaseBarrelBlockEntity; -import ru.bclib.blockentities.BaseChestBlockEntity; -import ru.bclib.blockentities.BaseFurnaceBlockEntity; -import ru.bclib.blockentities.BaseSignBlockEntity; -import ru.bclib.blockentities.DynamicBlockEntityType; -import ru.bclib.blockentities.DynamicBlockEntityType.BlockEntitySupplier; -import ru.bclib.blocks.BaseBarrelBlock; -import ru.bclib.blocks.BaseChestBlock; -import ru.bclib.blocks.BaseFurnaceBlock; -import ru.bclib.blocks.BaseSignBlock; - -public class BaseBlockEntities { - public static final DynamicBlockEntityType CHEST = registerBlockEntityType(BCLib.makeID("chest"), BaseChestBlockEntity::new); - public static final DynamicBlockEntityType BARREL = registerBlockEntityType(BCLib.makeID("barrel"), BaseBarrelBlockEntity::new); - public static final DynamicBlockEntityType SIGN = registerBlockEntityType(BCLib.makeID("sign"), BaseSignBlockEntity::new); - public static final DynamicBlockEntityType FURNACE = registerBlockEntityType(BCLib.makeID("furnace"), BaseFurnaceBlockEntity::new); - - public static DynamicBlockEntityType registerBlockEntityType(ResourceLocation typeId, BlockEntitySupplier supplier) { - return Registry.register(Registry.BLOCK_ENTITY_TYPE, typeId, new DynamicBlockEntityType<>(supplier)); - } - - public static void register() {} - - public static Block[] getChests() { - return Registry.BLOCK - .stream() - .filter(block -> block instanceof BaseChestBlock) - .toArray(Block[]::new); - } - - public static Block[] getBarrels() { - return Registry.BLOCK - .stream() - .filter(block -> block instanceof BaseBarrelBlock) - .toArray(Block[]::new); - } - - public static Block[] getSigns() { - return Registry.BLOCK - .stream() - .filter(block -> block instanceof BaseSignBlock) - .toArray(Block[]::new); - } - - public static Block[] getFurnaces() { - return Registry.BLOCK - .stream() - .filter(block -> block instanceof BaseFurnaceBlock) - .toArray(Block[]::new); - } -} diff --git a/src/main/java/ru/bclib/registry/BaseBlockEntityRenders.java b/src/main/java/ru/bclib/registry/BaseBlockEntityRenders.java deleted file mode 100644 index 98994667..00000000 --- a/src/main/java/ru/bclib/registry/BaseBlockEntityRenders.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.bclib.registry; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry; -import ru.bclib.client.render.BaseChestBlockEntityRenderer; -import ru.bclib.client.render.BaseSignBlockEntityRenderer; - -@Environment(EnvType.CLIENT) -public class BaseBlockEntityRenders { - public static void register() { - BlockEntityRendererRegistry.register(BaseBlockEntities.CHEST, BaseChestBlockEntityRenderer::new); - BlockEntityRendererRegistry.register(BaseBlockEntities.SIGN, BaseSignBlockEntityRenderer::new); - } -} diff --git a/src/main/java/ru/bclib/registry/BaseRegistry.java b/src/main/java/ru/bclib/registry/BaseRegistry.java deleted file mode 100644 index ea5a4fd7..00000000 --- a/src/main/java/ru/bclib/registry/BaseRegistry.java +++ /dev/null @@ -1,79 +0,0 @@ -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.resources.ResourceLocation; -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import ru.bclib.config.PathConfig; - -import java.util.List; -import java.util.Map; - -public abstract class BaseRegistry { - private static final List> REGISTRIES = Lists.newArrayList(); - private static final Map> MOD_BLOCK_ITEMS = Maps.newHashMap(); - private static final Map> MOD_BLOCKS = Maps.newHashMap(); - private static final Map> 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> getRegisteredBlocks() { - return MOD_BLOCK_ITEMS; - } - - public static Map> getRegisteredItems() { - return MOD_ITEMS; - } - - public static List getModBlockItems(String modId) { - if (MOD_BLOCK_ITEMS.containsKey(modId)) { - return MOD_BLOCK_ITEMS.get(modId); - } - List modBlocks = Lists.newArrayList(); - MOD_BLOCK_ITEMS.put(modId, modBlocks); - return modBlocks; - } - - public static List getModItems(String modId) { - if (MOD_ITEMS.containsKey(modId)) { - return MOD_ITEMS.get(modId); - } - List modBlocks = Lists.newArrayList(); - MOD_ITEMS.put(modId, modBlocks); - return modBlocks; - } - - public static List getModBlocks(String modId) { - if (MOD_BLOCKS.containsKey(modId)) { - return MOD_BLOCKS.get(modId); - } - List modBlocks = Lists.newArrayList(); - MOD_BLOCKS.put(modId, modBlocks); - return modBlocks; - } - - public static void register() { - REGISTRIES.forEach(BaseRegistry::registerInternal); - } -} diff --git a/src/main/java/ru/bclib/registry/BlockRegistry.java b/src/main/java/ru/bclib/registry/BlockRegistry.java deleted file mode 100644 index 4c56bab8..00000000 --- a/src/main/java/ru/bclib/registry/BlockRegistry.java +++ /dev/null @@ -1,88 +0,0 @@ -package ru.bclib.registry; - -import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; -import net.minecraft.core.Registry; -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.tag.*; -import ru.bclib.blocks.BaseLeavesBlock; -import ru.bclib.blocks.BaseOreBlock; -import ru.bclib.blocks.FeatureSaplingBlock; -import ru.bclib.config.PathConfig; -import ru.bclib.interfaces.CustomItemProvider; - -public class BlockRegistry extends BaseRegistry { - 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()); - } - else { - item = new BlockItem(block, makeItemSettings()); - } - registerBlockItem(id, item); - if (block.defaultBlockState().getMaterial().isFlammable() && FlammableBlockRegistry.getDefaultInstance().get(block).getBurnChance() == 0) { - FlammableBlockRegistry.getDefaultInstance().add(block, 5, 5); - } - - block = Registry.register(Registry.BLOCK, id, block); - getModBlocks(id.getNamespace()).add(block); - - if (block instanceof BaseLeavesBlock){ - TagAPI.addBlockTags( - block, - NamedBlockTags.LEAVES, - CommonBlockTags.LEAVES, - NamedMineableTags.HOE, - NamedMineableTags.SHEARS - ); - if (item != null){ - TagAPI.addItemTags(item, CommonItemTags.LEAVES, NamedItemTags.LEAVES); - } - } else if (block instanceof BaseOreBlock){ - TagAPI.addBlockTags(block, NamedMineableTags.PICKAXE); - } else if (block instanceof FeatureSaplingBlock){ - TagAPI.addBlockTags(block, CommonBlockTags.SAPLINGS, NamedBlockTags.SAPLINGS); - if (item != null){ - TagAPI.addItemTags(item, CommonItemTags.SAPLINGS, NamedItemTags.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); - 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); - } - } -} diff --git a/src/main/java/ru/bclib/registry/ItemRegistry.java b/src/main/java/ru/bclib/registry/ItemRegistry.java deleted file mode 100644 index 480a72f7..00000000 --- a/src/main/java/ru/bclib/registry/ItemRegistry.java +++ /dev/null @@ -1,160 +0,0 @@ -package ru.bclib.registry; - -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.world.effect.MobEffectInstance; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobSpawnType; -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.level.block.DispenserBlock; - -import ru.bclib.api.tag.CommonItemTags; -import ru.bclib.api.tag.NamedToolTags; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.config.PathConfig; -import ru.bclib.items.BaseDiscItem; -import ru.bclib.items.BaseDrinkItem; -import ru.bclib.items.BaseSpawnEggItem; -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 class ItemRegistry extends BaseRegistry { - public ItemRegistry(CreativeModeTab creativeTab, PathConfig config) { - super(creativeTab, config); - } - - 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; - } - register(itemId, item); - return item; - } - - public Item register(ResourceLocation itemId) { - return register(itemId, new ModelProviderItem(makeItemSettings())); - } - - @Override - public Item register(ResourceLocation itemId, Item item) { - if (!config.getBoolean("items", itemId.getPath(), true)) { - return item; - } - - registerItem(itemId, item); - - return item; - } - - public Item registerTool(ResourceLocation itemId, Item item) { - if (!config.getBoolean("tools", itemId.getPath(), true)) { - return item; - } - - registerItem(itemId, item); - - if (item instanceof ShovelItem) { - TagAPI.addItemTag(NamedToolTags.FABRIC_SHOVELS, item); - } - else if (item instanceof SwordItem) { - TagAPI.addItemTag(NamedToolTags.FABRIC_SWORDS, item); - } - else if (item instanceof BasePickaxeItem) { - TagAPI.addItemTag(NamedToolTags.FABRIC_PICKAXES, item); - } - else if (item instanceof BaseAxeItem) { - TagAPI.addItemTag(NamedToolTags.FABRIC_AXES, item); - } - else if (item instanceof BaseHoeItem) { - TagAPI.addItemTag(NamedToolTags.FABRIC_HOES, item); - } - else if (item instanceof BaseShearsItem) { - TagAPI.addItemTags(item, NamedToolTags.FABRIC_SHEARS, CommonItemTags.SHEARS); - DispenserBlock.registerBehavior(item.asItem(), new ShearsDispenseItemBehavior()); - } - - return item; - } - - public Item registerEgg(ResourceLocation itemId, EntityType 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); - EntityType entityType = ((SpawnEggItem) stack.getItem()).getType(stack.getTag()); - entityType.spawn( - pointer.getLevel(), - stack, - null, - pointer.getPos().relative(direction), - MobSpawnType.DISPENSER, - direction != Direction.UP, - false - ); - stack.shrink(1); - return stack; - } - }; - DispenserBlock.registerBehavior(item, behavior); - return register(itemId, item); - } - - 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(itemId, builder.build()); - } - - public Item registerFood(ResourceLocation itemId, FoodProperties foodComponent) { - return register(itemId, new ModelProviderItem(makeItemSettings().food(foodComponent))); - } - - public Item registerDrink(ResourceLocation itemId, FoodProperties foodComponent) { - return register(itemId, new BaseDrinkItem(makeItemSettings().stacksTo(1).food(foodComponent))); - } - - public Item registerDrink(ResourceLocation itemId, int hunger, float saturation) { - FoodProperties.Builder builder = new FoodProperties.Builder().nutrition(hunger).saturationMod(saturation); - 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; - } -} diff --git a/src/main/java/ru/bclib/sdf/PosInfo.java b/src/main/java/ru/bclib/sdf/PosInfo.java deleted file mode 100644 index d7e7e0ed..00000000 --- a/src/main/java/ru/bclib/sdf/PosInfo.java +++ /dev/null @@ -1,104 +0,0 @@ -package ru.bclib.sdf; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; - -import java.util.Map; - -public class PosInfo implements Comparable { - private static final BlockState AIR = Blocks.AIR.defaultBlockState(); - private final Map blocks; - private final Map add; - private final BlockPos pos; - private BlockState state; - - public static PosInfo create(Map blocks, Map add, BlockPos pos) { - return new PosInfo(blocks, add, pos); - } - - private PosInfo(Map blocks, Map add, BlockPos pos) { - this.blocks = blocks; - this.add = add; - this.pos = pos; - blocks.put(pos, this); - } - - public BlockState getState() { - return state; - } - - public BlockState getState(BlockPos pos) { - PosInfo info = blocks.get(pos); - if (info == null) { - info = add.get(pos); - return info == null ? AIR : info.getState(); - } - return info.getState(); - } - - public void setState(BlockState state) { - this.state = state; - } - - public void setState(BlockPos pos, BlockState state) { - PosInfo info = blocks.get(pos); - if (info != null) { - info.setState(state); - } - } - - public BlockState getState(Direction dir) { - PosInfo info = blocks.get(pos.relative(dir)); - if (info == null) { - info = add.get(pos.relative(dir)); - return info == null ? AIR : info.getState(); - } - return info.getState(); - } - - public BlockState getState(Direction dir, int distance) { - PosInfo info = blocks.get(pos.relative(dir, distance)); - if (info == null) { - return AIR; - } - return info.getState(); - } - - public BlockState getStateUp() { - return getState(Direction.UP); - } - - public BlockState getStateDown() { - return getState(Direction.DOWN); - } - - @Override - public int hashCode() { - return pos.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PosInfo)) { - return false; - } - return pos.equals(((PosInfo) obj).pos); - } - - @Override - public int compareTo(PosInfo info) { - return this.pos.getY() - info.pos.getY(); - } - - public BlockPos getPos() { - return pos; - } - - public void setBlockPos(BlockPos pos, BlockState state) { - PosInfo info = new PosInfo(blocks, add, pos); - info.state = state; - add.put(pos, info); - } -} diff --git a/src/main/java/ru/bclib/sdf/SDF.java b/src/main/java/ru/bclib/sdf/SDF.java deleted file mode 100644 index 57d7ab31..00000000 --- a/src/main/java/ru/bclib/sdf/SDF.java +++ /dev/null @@ -1,309 +0,0 @@ -package ru.bclib.sdf; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.ServerLevelAccessor; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import ru.bclib.util.BlocksHelper; -import ru.bclib.world.structures.StructureWorld; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -public abstract class SDF { - private List> postProcesses = Lists.newArrayList(); - private Function canReplace = (state) -> { - return state.getMaterial().isReplaceable(); - }; - - public abstract float getDistance(float x, float y, float z); - - public abstract BlockState getBlockState(BlockPos pos); - - public SDF addPostProcess(Function postProcess) { - this.postProcesses.add(postProcess); - return this; - } - - public SDF setReplaceFunction(Function canReplace) { - this.canReplace = canReplace; - return this; - } - - public void fillRecursive(ServerLevelAccessor world, BlockPos start) { - Map mapWorld = Maps.newHashMap(); - Map addInfo = Maps.newHashMap(); - Set blocks = Sets.newHashSet(); - Set ends = Sets.newHashSet(); - Set add = Sets.newHashSet(); - ends.add(new BlockPos(0, 0, 0)); - boolean run = true; - - MutableBlockPos bPos = new MutableBlockPos(); - - while (run) { - for (BlockPos center : ends) { - for (Direction dir : Direction.values()) { - bPos.set(center).move(dir); - BlockPos wpos = bPos.offset(start); - - if (!blocks.contains(bPos) && canReplace.apply(world.getBlockState(wpos))) { - if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { - BlockState state = getBlockState(wpos); - PosInfo.create(mapWorld, addInfo, wpos).setState(state); - add.add(bPos.immutable()); - } - } - } - } - - blocks.addAll(ends); - ends.clear(); - ends.addAll(add); - add.clear(); - - run &= !ends.isEmpty(); - } - - List infos = new ArrayList(mapWorld.values()); - if (infos.size() > 0) { - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); - }); - - infos.clear(); - infos.addAll(addInfo.values()); - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - if (canReplace.apply(world.getBlockState(info.getPos()))) { - BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); - } - }); - } - } - - public void fillArea(ServerLevelAccessor world, BlockPos center, AABB box) { - Map mapWorld = Maps.newHashMap(); - Map addInfo = Maps.newHashMap(); - - MutableBlockPos mut = new MutableBlockPos(); - for (int y = (int) box.minY; y <= box.maxY; y++) { - mut.setY(y); - for (int x = (int) box.minX; x <= box.maxX; x++) { - mut.setX(x); - for (int z = (int) box.minZ; z <= box.maxZ; z++) { - mut.setZ(z); - if (canReplace.apply(world.getBlockState(mut))) { - BlockPos fpos = mut.subtract(center); - if (this.getDistance(fpos.getX(), fpos.getY(), fpos.getZ()) < 0) { - PosInfo.create(mapWorld, addInfo, mut.immutable()).setState(getBlockState(mut)); - } - } - } - } - } - - List infos = new ArrayList(mapWorld.values()); - if (infos.size() > 0) { - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); - }); - - infos.clear(); - infos.addAll(addInfo.values()); - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - if (canReplace.apply(world.getBlockState(info.getPos()))) { - BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); - } - }); - } - } - - public void fillRecursiveIgnore(ServerLevelAccessor world, BlockPos start, Function ignore) { - Map mapWorld = Maps.newHashMap(); - Map addInfo = Maps.newHashMap(); - Set blocks = Sets.newHashSet(); - Set ends = Sets.newHashSet(); - Set add = Sets.newHashSet(); - ends.add(new BlockPos(0, 0, 0)); - boolean run = true; - - MutableBlockPos bPos = new MutableBlockPos(); - - while (run) { - for (BlockPos center : ends) { - for (Direction dir : Direction.values()) { - bPos.set(center).move(dir); - BlockPos wpos = bPos.offset(start); - BlockState state = world.getBlockState(wpos); - boolean ign = ignore.apply(state); - if (!blocks.contains(bPos) && (ign || canReplace.apply(state))) { - if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { - PosInfo.create(mapWorld, addInfo, wpos).setState(ign ? state : getBlockState(bPos)); - add.add(bPos.immutable()); - } - } - } - } - - blocks.addAll(ends); - ends.clear(); - ends.addAll(add); - add.clear(); - - run &= !ends.isEmpty(); - } - - List infos = new ArrayList(mapWorld.values()); - if (infos.size() > 0) { - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); - }); - - infos.clear(); - infos.addAll(addInfo.values()); - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - if (canReplace.apply(world.getBlockState(info.getPos()))) { - BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); - } - }); - } - } - - public void fillRecursive(StructureWorld world, BlockPos start) { - Map mapWorld = Maps.newHashMap(); - Map addInfo = Maps.newHashMap(); - Set blocks = Sets.newHashSet(); - Set ends = Sets.newHashSet(); - Set add = Sets.newHashSet(); - ends.add(new BlockPos(0, 0, 0)); - boolean run = true; - - MutableBlockPos bPos = new MutableBlockPos(); - - while (run) { - for (BlockPos center : ends) { - for (Direction dir : Direction.values()) { - bPos.set(center).move(dir); - BlockPos wpos = bPos.offset(start); - - if (!blocks.contains(bPos)) { - if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { - BlockState state = getBlockState(wpos); - PosInfo.create(mapWorld, addInfo, wpos).setState(state); - add.add(bPos.immutable()); - } - } - } - } - - blocks.addAll(ends); - ends.clear(); - ends.addAll(add); - add.clear(); - - run &= !ends.isEmpty(); - } - - List infos = new ArrayList(mapWorld.values()); - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - world.setBlock(info.getPos(), info.getState()); - }); - - infos.clear(); - infos.addAll(addInfo.values()); - Collections.sort(infos); - postProcesses.forEach((postProcess) -> { - infos.forEach((info) -> { - info.setState(postProcess.apply(info)); - }); - }); - infos.forEach((info) -> { - world.setBlock(info.getPos(), info.getState()); - }); - } - - public Set getPositions(ServerLevelAccessor world, BlockPos start) { - Set blocks = Sets.newHashSet(); - Set ends = Sets.newHashSet(); - Set add = Sets.newHashSet(); - ends.add(new BlockPos(0, 0, 0)); - boolean run = true; - - MutableBlockPos bPos = new MutableBlockPos(); - - while (run) { - for (BlockPos center : ends) { - for (Direction dir : Direction.values()) { - bPos.set(center).move(dir); - BlockPos wpos = bPos.offset(start); - BlockState state = world.getBlockState(wpos); - if (!blocks.contains(wpos) && canReplace.apply(state)) { - if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { - add.add(bPos.immutable()); - } - } - } - } - - ends.forEach((end) -> blocks.add(end.offset(start))); - ends.clear(); - ends.addAll(add); - add.clear(); - - run &= !ends.isEmpty(); - } - - return blocks; - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFBinary.java b/src/main/java/ru/bclib/sdf/operator/SDFBinary.java deleted file mode 100644 index 056aba3f..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFBinary.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.bclib.sdf.operator; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.sdf.SDF; - -public abstract class SDFBinary extends SDF { - protected SDF sourceA; - protected SDF sourceB; - protected boolean firstValue; - - public SDFBinary setSourceA(SDF sourceA) { - this.sourceA = sourceA; - return this; - } - - public SDFBinary setSourceB(SDF sourceB) { - this.sourceB = sourceB; - return this; - } - - protected void selectValue(float a, float b) { - firstValue = a < b; - } - - @Override - public BlockState getBlockState(BlockPos pos) { - if (firstValue) { - return sourceA.getBlockState(pos); - } - else { - return sourceB.getBlockState(pos); - } - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java b/src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java deleted file mode 100644 index e742060a..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.bclib.sdf.operator; - -import com.mojang.math.Vector3f; - -import java.util.function.Consumer; - -public class SDFCoordModify extends SDFUnary { - private final Vector3f pos = new Vector3f(); - private Consumer function; - - public SDFCoordModify setFunction(Consumer function) { - this.function = function; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - pos.set(x, y, z); - function.accept(pos); - return this.source.getDistance(pos.x(), pos.y(), pos.z()); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java b/src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java deleted file mode 100644 index 2960f254..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.bclib.sdf.operator; - -import ru.bclib.util.MHelper; - -public class SDFCopyRotate extends SDFUnary { - int count = 1; - - public SDFCopyRotate setCount(int count) { - this.count = count; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float px = (float) Math.atan2(x, z); - float pz = MHelper.length(x, z); - return this.source.getDistance(px, y, pz); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java b/src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java deleted file mode 100644 index 272a487a..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.sdf.operator; - -import com.mojang.math.Vector3f; - -import java.util.function.Function; - -public class SDFDisplacement extends SDFUnary { - private final Vector3f pos = new Vector3f(); - private Function displace; - - public SDFDisplacement setFunction(Function displace) { - this.displace = displace; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - pos.set(x, y, z); - return this.source.getDistance(x, y, z) + displace.apply(pos); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java b/src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java deleted file mode 100644 index d4d2174d..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.bclib.sdf.operator; - -public class SDFFlatWave extends SDFDisplacement { - private int rayCount = 1; - private float intensity; - private float angle; - - public SDFFlatWave() { - setFunction((pos) -> { - return (float) Math.cos(Math.atan2(pos.x(), pos.z()) * rayCount + angle) * intensity; - }); - } - - public SDFFlatWave setRaysCount(int count) { - this.rayCount = count; - return this; - } - - public SDFFlatWave setAngle(float angle) { - this.angle = angle; - return this; - } - - public SDFFlatWave setIntensity(float intensity) { - this.intensity = intensity; - return this; - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java b/src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java deleted file mode 100644 index b5233d51..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java +++ /dev/null @@ -1,63 +0,0 @@ -package ru.bclib.sdf.operator; - -import com.mojang.blaze3d.platform.NativeImage; -import net.minecraft.util.Mth; - -public class SDFHeightmap extends SDFDisplacement { - private float intensity = 1F; - private NativeImage map; - private float offsetX; - private float offsetZ; - private float scale; - private float cos = 1; - private float sin = 0; - - public SDFHeightmap() { - setFunction((pos) -> { - if (map == null) { - return 0F; - } - float px = Mth.clamp(pos.x() * scale + offsetX, 0, map.getWidth() - 2); - float pz = Mth.clamp(pos.z() * scale + offsetZ, 0, map.getHeight() - 2); - float dx = (px * cos - pz * sin); - float dz = (pz * cos + px * sin); - int x1 = Mth.floor(dx); - int z1 = Mth.floor(dz); - int x2 = x1 + 1; - int z2 = z1 + 1; - dx = dx - x1; - dz = dz - z1; - float a = (map.getPixelRGBA(x1, z1) & 255) / 255F; - float b = (map.getPixelRGBA(x2, z1) & 255) / 255F; - float c = (map.getPixelRGBA(x1, z2) & 255) / 255F; - float d = (map.getPixelRGBA(x2, z2) & 255) / 255F; - a = Mth.lerp(dx, a, b); - b = Mth.lerp(dx, c, d); - return -Mth.lerp(dz, a, b) * intensity; - }); - } - - public SDFHeightmap setMap(NativeImage map) { - this.map = map; - offsetX = map.getWidth() * 0.5F; - offsetZ = map.getHeight() * 0.5F; - scale = map.getWidth(); - return this; - } - - public SDFHeightmap setAngle(float angle) { - sin = Mth.sin(angle); - cos = Mth.cos(angle); - return this; - } - - public SDFHeightmap setScale(float scale) { - this.scale = map.getWidth() * scale; - return this; - } - - public SDFHeightmap setIntensity(float intensity) { - this.intensity = intensity; - return this; - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFIntersection.java b/src/main/java/ru/bclib/sdf/operator/SDFIntersection.java deleted file mode 100644 index d5ef436c..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFIntersection.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.bclib.sdf.operator; - -import ru.bclib.util.MHelper; - -public class SDFIntersection extends SDFBinary { - @Override - public float getDistance(float x, float y, float z) { - float a = this.sourceA.getDistance(x, y, z); - float b = this.sourceB.getDistance(x, y, z); - this.selectValue(a, b); - return MHelper.max(a, b); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFInvert.java b/src/main/java/ru/bclib/sdf/operator/SDFInvert.java deleted file mode 100644 index b6cb1be0..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFInvert.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.bclib.sdf.operator; - -public class SDFInvert extends SDFUnary { - @Override - public float getDistance(float x, float y, float z) { - return -this.source.getDistance(x, y, z); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java b/src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java deleted file mode 100644 index 3e3be641..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java +++ /dev/null @@ -1,63 +0,0 @@ -package ru.bclib.sdf.operator; - -import net.minecraft.util.Mth; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.util.MHelper; - -public class SDFRadialNoiseMap extends SDFDisplacement { - private static final float SIN = Mth.sin(0.5F); - private static final float COS = Mth.cos(0.5F); - - private OpenSimplexNoise noise; - private float intensity = 1F; - private float radius = 1F; - private short offsetX; - private short offsetZ; - - public SDFRadialNoiseMap() { - setFunction((pos) -> { - if (intensity == 0) { - return 0F; - } - float px = pos.x() / radius; - float pz = pos.z() / radius; - float distance = MHelper.lengthSqr(px, pz); - if (distance > 1) { - return 0F; - } - distance = 1 - Mth.sqrt(distance); - float nx = px * COS - pz * SIN; - float nz = pz * COS + px * SIN; - distance *= getNoise(nx * 0.75 + offsetX, nz * 0.75 + offsetZ); - return distance * intensity; - }); - } - - private float getNoise(double x, double z) { - return (float) noise.eval(x, z) + (float) noise.eval( - x * 3 + 1000, - z * 3 - ) * 0.5F + (float) noise.eval(x * 9 + 1000, z * 9) * 0.2F; - } - - public SDFRadialNoiseMap setSeed(long seed) { - noise = new OpenSimplexNoise(seed); - return this; - } - - public SDFRadialNoiseMap setIntensity(float intensity) { - this.intensity = intensity; - return this; - } - - public SDFRadialNoiseMap setRadius(float radius) { - this.radius = radius; - return this; - } - - public SDFRadialNoiseMap setOffset(int x, int z) { - offsetX = (short) (x & 32767); - offsetZ = (short) (z & 32767); - return this; - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFRotation.java b/src/main/java/ru/bclib/sdf/operator/SDFRotation.java deleted file mode 100644 index a4474993..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFRotation.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.sdf.operator; - -import com.mojang.math.Quaternion; -import com.mojang.math.Vector3f; - -public class SDFRotation extends SDFUnary { - private final Vector3f pos = new Vector3f(); - private Quaternion rotation; - - public SDFRotation setRotation(Vector3f axis, float rotationAngle) { - rotation = new Quaternion(axis, rotationAngle, false); - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - pos.set(x, y, z); - pos.transform(rotation); - return source.getDistance(pos.x(), pos.y(), pos.z()); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFRound.java b/src/main/java/ru/bclib/sdf/operator/SDFRound.java deleted file mode 100644 index fd5b0e51..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFRound.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.bclib.sdf.operator; - -public class SDFRound extends SDFUnary { - private float radius; - - public SDFRound setRadius(float radius) { - this.radius = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - return this.source.getDistance(x, y, z) - radius; - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFScale.java b/src/main/java/ru/bclib/sdf/operator/SDFScale.java deleted file mode 100644 index 18ee0eaf..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFScale.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.bclib.sdf.operator; - -public class SDFScale extends SDFUnary { - private float scale; - - public SDFScale setScale(float scale) { - this.scale = scale; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - return source.getDistance(x / scale, y / scale, z / scale) * scale; - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFScale3D.java b/src/main/java/ru/bclib/sdf/operator/SDFScale3D.java deleted file mode 100644 index 3693d751..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFScale3D.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.bclib.sdf.operator; - -public class SDFScale3D extends SDFUnary { - private float x; - private float y; - private float z; - - public SDFScale3D setScale(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - return source.getDistance(x / this.x, y / this.y, z / this.z); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSmoothIntersection.java b/src/main/java/ru/bclib/sdf/operator/SDFSmoothIntersection.java deleted file mode 100644 index 5770d25b..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFSmoothIntersection.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.sdf.operator; - -import net.minecraft.util.Mth; - -public class SDFSmoothIntersection extends SDFBinary { - private float radius; - - public SDFSmoothIntersection setRadius(float radius) { - this.radius = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float a = this.sourceA.getDistance(x, y, z); - float b = this.sourceB.getDistance(x, y, z); - this.selectValue(a, b); - float h = Mth.clamp(0.5F - 0.5F * (b - a) / radius, 0F, 1F); - return Mth.lerp(h, b, a) + radius * h * (1F - h); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSmoothSubtraction.java b/src/main/java/ru/bclib/sdf/operator/SDFSmoothSubtraction.java deleted file mode 100644 index c7ac6e73..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFSmoothSubtraction.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.sdf.operator; - -import net.minecraft.util.Mth; - -public class SDFSmoothSubtraction extends SDFBinary { - private float radius; - - public SDFSmoothSubtraction setRadius(float radius) { - this.radius = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float a = this.sourceA.getDistance(x, y, z); - float b = this.sourceB.getDistance(x, y, z); - this.selectValue(a, b); - float h = Mth.clamp(0.5F - 0.5F * (b + a) / radius, 0F, 1F); - return Mth.lerp(h, b, -a) + radius * h * (1F - h); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java b/src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java deleted file mode 100644 index e7034e75..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.bclib.sdf.operator; - -import net.minecraft.util.Mth; - -public class SDFSmoothUnion extends SDFBinary { - private float radius; - - public SDFSmoothUnion setRadius(float radius) { - this.radius = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float a = this.sourceA.getDistance(x, y, z); - float b = this.sourceB.getDistance(x, y, z); - this.selectValue(a, b); - float h = Mth.clamp(0.5F + 0.5F * (b - a) / radius, 0F, 1F); - return Mth.lerp(h, b, a) - radius * h * (1F - h); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java b/src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java deleted file mode 100644 index d0356868..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.bclib.sdf.operator; - -import ru.bclib.util.MHelper; - -public class SDFSubtraction extends SDFBinary { - @Override - public float getDistance(float x, float y, float z) { - float a = this.sourceA.getDistance(x, y, z); - float b = this.sourceB.getDistance(x, y, z); - this.selectValue(a, b); - return MHelper.max(a, -b); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFTranslate.java b/src/main/java/ru/bclib/sdf/operator/SDFTranslate.java deleted file mode 100644 index 99168225..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFTranslate.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.bclib.sdf.operator; - -public class SDFTranslate extends SDFUnary { - float x; - float y; - float z; - - public SDFTranslate setTranslate(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - return source.getDistance(x - this.x, y - this.y, z - this.z); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFUnary.java b/src/main/java/ru/bclib/sdf/operator/SDFUnary.java deleted file mode 100644 index 83882752..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFUnary.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.bclib.sdf.operator; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.sdf.SDF; - -public abstract class SDFUnary extends SDF { - protected SDF source; - - public SDFUnary setSource(SDF source) { - this.source = source; - return this; - } - - @Override - public BlockState getBlockState(BlockPos pos) { - return source.getBlockState(pos); - } -} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFUnion.java b/src/main/java/ru/bclib/sdf/operator/SDFUnion.java deleted file mode 100644 index b9db5ab0..00000000 --- a/src/main/java/ru/bclib/sdf/operator/SDFUnion.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.bclib.sdf.operator; - -import ru.bclib.util.MHelper; - -public class SDFUnion extends SDFBinary { - @Override - public float getDistance(float x, float y, float z) { - float a = this.sourceA.getDistance(x, y, z); - float b = this.sourceB.getDistance(x, y, z); - this.selectValue(a, b); - return MHelper.min(a, b); - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java b/src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java deleted file mode 100644 index bd3230ba..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.bclib.sdf.primitive; - -import net.minecraft.util.Mth; -import ru.bclib.util.MHelper; - -public class SDFCappedCone extends SDFPrimitive { - private float radius1; - private float radius2; - private float height; - - public SDFCappedCone setRadius1(float radius) { - this.radius1 = radius; - return this; - } - - public SDFCappedCone setRadius2(float radius) { - this.radius2 = radius; - return this; - } - - public SDFCappedCone setHeight(float height) { - this.height = height; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float qx = MHelper.length(x, z); - float k2x = radius2 - radius1; - float k2y = 2 * height; - float cax = qx - MHelper.min(qx, (y < 0F) ? radius1 : radius2); - float cay = Math.abs(y) - height; - float mlt = Mth.clamp( - MHelper.dot(radius2 - qx, height - y, k2x, k2y) / MHelper.dot(k2x, k2y, k2x, k2y), - 0F, - 1F - ); - float cbx = qx - radius2 + k2x * mlt; - float cby = y - height + k2y * mlt; - float s = (cbx < 0F && cay < 0F) ? -1F : 1F; - return s * (float) Math.sqrt(MHelper.min(MHelper.dot(cax, cay, cax, cay), MHelper.dot(cbx, cby, cbx, cby))); - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java b/src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java deleted file mode 100644 index a29f724e..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.bclib.sdf.primitive; - -import net.minecraft.util.Mth; -import ru.bclib.util.MHelper; - -public class SDFCapsule extends SDFPrimitive { - private float radius; - private float height; - - public SDFCapsule setRadius(float radius) { - this.radius = radius; - return this; - } - - public SDFCapsule setHeight(float height) { - this.height = height; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - return MHelper.length(x, y - Mth.clamp(y, 0, height), z) - radius; - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java b/src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java deleted file mode 100644 index 586aaf73..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.bclib.sdf.primitive; - -public class SDFFlatland extends SDFPrimitive { - @Override - public float getDistance(float x, float y, float z) { - return y; - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java b/src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java deleted file mode 100644 index a2e28a83..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.bclib.sdf.primitive; - -import ru.bclib.util.MHelper; - -public class SDFHexPrism extends SDFPrimitive { - private float radius; - private float height; - - public SDFHexPrism setRadius(float radius) { - this.radius = radius; - return this; - } - - public SDFHexPrism setHeight(float height) { - this.height = height; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float px = Math.abs(x); - float py = Math.abs(y); - float pz = Math.abs(z); - return MHelper.max(py - height, MHelper.max((px * 0.866025F + pz * 0.5F), pz) - radius); - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFLine.java b/src/main/java/ru/bclib/sdf/primitive/SDFLine.java deleted file mode 100644 index 0c166a91..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFLine.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.bclib.sdf.primitive; - -import net.minecraft.util.Mth; -import ru.bclib.util.MHelper; - -public class SDFLine extends SDFPrimitive { - private float radius; - private float x1; - private float y1; - private float z1; - private float x2; - private float y2; - private float z2; - - public SDFLine setRadius(float radius) { - this.radius = radius; - return this; - } - - public SDFLine setStart(float x, float y, float z) { - this.x1 = x; - this.y1 = y; - this.z1 = z; - return this; - } - - public SDFLine setEnd(float x, float y, float z) { - this.x2 = x; - this.y2 = y; - this.z2 = z; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float pax = x - x1; - float pay = y - y1; - float paz = z - z1; - - float bax = x2 - x1; - float bay = y2 - y1; - float baz = z2 - z1; - - float dpb = MHelper.dot(pax, pay, paz, bax, bay, baz); - float dbb = MHelper.dot(bax, bay, baz, bax, bay, baz); - float h = Mth.clamp(dpb / dbb, 0F, 1F); - return MHelper.length(pax - bax * h, pay - bay * h, paz - baz * h) - radius; - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFPie.java b/src/main/java/ru/bclib/sdf/primitive/SDFPie.java deleted file mode 100644 index 6bff34ea..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFPie.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.bclib.sdf.primitive; - -import net.minecraft.util.Mth; -import ru.bclib.util.MHelper; - -public class SDFPie extends SDFPrimitive { - private float sin; - private float cos; - private float radius; - - public SDFPie setAngle(float angle) { - this.sin = (float) Math.sin(angle); - this.cos = (float) Math.cos(angle); - return this; - } - - public SDFPie setRadius(float radius) { - this.radius = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float px = Math.abs(x); - float l = MHelper.length(px, y, z) - radius; - float m = MHelper.dot(px, z, sin, cos); - m = Mth.clamp(m, 0, radius); - m = MHelper.length(px - sin * m, z - cos * m); - return MHelper.max(l, m * (float) Math.signum(cos * px - sin * z)); - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java b/src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java deleted file mode 100644 index 4e3b527f..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.bclib.sdf.primitive; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.sdf.SDF; - -import java.util.function.Function; - -public abstract class SDFPrimitive extends SDF { - protected Function placerFunction; - - public SDFPrimitive setBlock(Function placerFunction) { - this.placerFunction = placerFunction; - return this; - } - - public SDFPrimitive setBlock(BlockState state) { - this.placerFunction = (pos) -> { - return state; - }; - return this; - } - - public SDFPrimitive setBlock(Block block) { - this.placerFunction = (pos) -> { - return block.defaultBlockState(); - }; - return this; - } - - public BlockState getBlockState(BlockPos pos) { - return placerFunction.apply(pos); - } - - /*public abstract CompoundTag toNBT(CompoundTag root) { - - }*/ -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFSphere.java b/src/main/java/ru/bclib/sdf/primitive/SDFSphere.java deleted file mode 100644 index edeb8ea2..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFSphere.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.bclib.sdf.primitive; - -import ru.bclib.util.MHelper; - -public class SDFSphere extends SDFPrimitive { - private float radius; - - public SDFSphere setRadius(float radius) { - this.radius = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - return MHelper.length(x, y, z) - radius; - } -} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFTorus.java b/src/main/java/ru/bclib/sdf/primitive/SDFTorus.java deleted file mode 100644 index e7540c93..00000000 --- a/src/main/java/ru/bclib/sdf/primitive/SDFTorus.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.bclib.sdf.primitive; - -import ru.bclib.util.MHelper; - -public class SDFTorus extends SDFPrimitive { - private float radiusSmall; - private float radiusBig; - - public SDFTorus setBigRadius(float radius) { - this.radiusBig = radius; - return this; - } - - public SDFTorus setSmallRadius(float radius) { - this.radiusSmall = radius; - return this; - } - - @Override - public float getDistance(float x, float y, float z) { - float nx = MHelper.length(x, z) - radiusBig; - return MHelper.length(nx, y) - radiusSmall; - } -} diff --git a/src/main/java/ru/bclib/server/BCLibServer.java b/src/main/java/ru/bclib/server/BCLibServer.java deleted file mode 100644 index e76b1147..00000000 --- a/src/main/java/ru/bclib/server/BCLibServer.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.bclib.server; - -import net.fabricmc.api.DedicatedServerModInitializer; -import ru.bclib.api.ModIntegrationAPI; -import ru.bclib.api.PostInitAPI; -import ru.bclib.api.dataexchange.DataExchangeAPI; - -public class BCLibServer implements DedicatedServerModInitializer { - @Override - public void onInitializeServer() { - ModIntegrationAPI.registerAll(); - DataExchangeAPI.prepareServerside(); - - PostInitAPI.postInit(false); - } -} diff --git a/src/main/java/ru/bclib/util/BackgroundInfo.java b/src/main/java/ru/bclib/util/BackgroundInfo.java deleted file mode 100644 index 34c2c347..00000000 --- a/src/main/java/ru/bclib/util/BackgroundInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.bclib.util; - -public class BackgroundInfo { - public static float fogColorRed; - public static float fogColorGreen; - public static float fogColorBlue; - public static float fogDensity = 1; - public static float blindness; -} diff --git a/src/main/java/ru/bclib/util/BlocksHelper.java b/src/main/java/ru/bclib/util/BlocksHelper.java deleted file mode 100644 index 042d8102..00000000 --- a/src/main/java/ru/bclib/util/BlocksHelper.java +++ /dev/null @@ -1,203 +0,0 @@ -package ru.bclib.util; - -import com.google.common.collect.Maps; -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ClipContext.Fluid; -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.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; - -import java.util.Map; -import java.util.Random;import net.minecraft.util.RandomSource; - -public class BlocksHelper { - private static final Map COLOR_BY_BLOCK = Maps.newHashMap(); - - public static final int FLAG_UPDATE_BLOCK = 1; - public static final int FLAG_SEND_CLIENT_CHANGES = 2; - public static final int FLAG_NO_RERENDER = 4; - public static final int FORSE_RERENDER = 8; - public static final int FLAG_IGNORE_OBSERVERS = 16; - - public static final int SET_SILENT = FLAG_UPDATE_BLOCK | FLAG_IGNORE_OBSERVERS | FLAG_SEND_CLIENT_CHANGES; - public static final int SET_OBSERV = FLAG_UPDATE_BLOCK | FLAG_SEND_CLIENT_CHANGES; - public static final Direction[] HORIZONTAL = makeHorizontal(); - public static final Direction[] DIRECTIONS = Direction.values(); - - private static final ThreadLocal TL_POS = ThreadLocal.withInitial(() -> new MutableBlockPos()); - - protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); - protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); - - public static void addBlockColor(Block block, int color) { - COLOR_BY_BLOCK.put(block, color); - } - - public static int getBlockColor(Block block) { - return COLOR_BY_BLOCK.getOrDefault(block, 0xFF000000); - } - - public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, BlockState state) { - world.setBlock(pos, state, SET_SILENT); - } - - public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, Block block) { - world.setBlock(pos, block.defaultBlockState(), SET_SILENT); - } - - public static void setWithUpdate(LevelAccessor world, BlockPos pos, BlockState state) { - world.setBlock(pos, state, SET_OBSERV); - } - - public static void setWithUpdate(LevelAccessor world, BlockPos pos, Block block) { - world.setBlock(pos, block.defaultBlockState(), SET_OBSERV); - } - - public static int upRay(LevelAccessor world, BlockPos pos, int maxDist) { - int length = 0; - for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.above(j))); j++) { - length++; - } - return length; - } - - public static int downRay(LevelAccessor world, BlockPos pos, int maxDist) { - int length = 0; - for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.below(j))); j++) { - length++; - } - return length; - } - - public static int downRayRep(LevelAccessor world, BlockPos pos, int maxDist) { - final MutableBlockPos POS = TL_POS.get(); - POS.set(pos); - for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) { - POS.setY(POS.getY() - 1); - } - return pos.getY() - POS.getY(); - } - - public static int raycastSqr(LevelAccessor world, BlockPos pos, int dx, int dy, int dz, int maxDist) { - final MutableBlockPos POS = TL_POS.get(); - POS.set(pos); - for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) { - POS.move(dx, dy, dz); - } - return (int) pos.distSqr(POS); - } - - /** - * Rotates {@link BlockState} horizontally. Used in block classes with {@link Direction} {@link Property} in rotate function. - * - * @param state - {@link BlockState} to mirror; - * @param rotation - {@link Rotation}; - * @param facing - Block {@link Direction} {@link Property}. - * @return Rotated {@link BlockState}. - */ - public static BlockState rotateHorizontal(BlockState state, Rotation rotation, Property facing) { - return state.setValue(facing, rotation.rotate(state.getValue(facing))); - } - - /** - * Mirrors {@link BlockState} horizontally. Used in block classes with {@link Direction} {@link Property} in mirror function. - * - * @param state - {@link BlockState} to mirror; - * @param mirror - {@link Mirror}; - * @param facing - Block {@link Direction} {@link Property}. - * @return Mirrored {@link BlockState}. - */ - public static BlockState mirrorHorizontal(BlockState state, Mirror mirror, Property facing) { - return state.rotate(mirror.getRotation(state.getValue(facing))); - } - - /** - * Counts the amount of same block down. - * - * @param world - {@link LevelAccessor} world; - * @param pos - {@link BlockPos} start position; - * @param block - {@link Block} to count. - * @return Integer amount of blocks. - */ - public static int getLengthDown(LevelAccessor world, BlockPos pos, Block block) { - int count = 1; - while (world.getBlockState(pos.below(count)).getBlock() == block) { - count++; - } - return count; - } - - /** - * Creates a new {@link Direction} array with clockwise order: - * NORTH, EAST, SOUTH, WEST - * - * @return Array of {@link Direction}. - */ - public static Direction[] makeHorizontal() { - return new Direction[] {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST}; - } - - /** - * Get any random horizontal {@link Direction}. - * - * @param random - {@link Random}. - * @return {@link Direction}. - */ - public static Direction randomHorizontal(RandomSource random) { - return HORIZONTAL[random.nextInt(4)]; - } - - /** - * Get any random {@link Direction} including vertical and horizontal. - * - * @param random - {@link Random}. - * @return {@link Direction}. - */ - public static Direction randomDirection(RandomSource random) { - return DIRECTIONS[random.nextInt(6)]; - } - - /** - * Check if block is {@link Fluid} or not. - * - * @param state - {@link BlockState} to check. - * @return {@code true} if block is fluid and {@code false} if not. - */ - public static boolean isFluid(BlockState state) { - return !state.getFluidState().isEmpty(); - } - - /** - * Check if block is "invulnerable" like Bedrock. - * - * @param state - {@link BlockState} to check; - * @param world - {@link BlockGetter} world where BlockState exist; - * @param pos - {@link BlockPos} where BlockState is. - * @return {@code true} if block is "invulnerable" and {@code false} if not. - */ - public static boolean isInvulnerable(BlockState state, BlockGetter world, BlockPos pos) { - return state.getDestroySpeed(world, pos) < 0; - } - - /** - * Check if block is "invulnerable" like Bedrock. Unlike safe function will pass world and position parameters as {@code null}. - * - * @param state - {@link BlockState} to check. - * @return {@code true} if block is "invulnerable" and {@code false} if not. - */ - public static boolean isInvulnerableUnsafe(BlockState state) { - try { - return isInvulnerable(state, null, null); - } - catch (Exception e) { - return false; - } - } -} diff --git a/src/main/java/ru/bclib/util/CollectionsUtil.java b/src/main/java/ru/bclib/util/CollectionsUtil.java deleted file mode 100644 index e7b472c9..00000000 --- a/src/main/java/ru/bclib/util/CollectionsUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -package ru.bclib.util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class CollectionsUtil { - /** - * Will return mutable copy of list. - * @param list {@link List} to make mutable. - * @return {@link ArrayList} or original {@link List} if it is mutable. - */ - public static List getMutable(List list) { - if (list instanceof ArrayList) { - return list; - } - return new ArrayList<>(list); - } - - /** - * Will return mutable copy of set. - * @param set {@link Set} to make mutable. - * @return {@link HashSet} or original {@link Set} if it is mutable. - */ - public static Set getMutable(Set set) { - if (set instanceof HashSet) { - return set; - } - return new HashSet<>(set); - } - - /** - * Will return mutable copy of map. - * @param map {@link Map} to make mutable. - * @return {@link HashMap} or original {@link Map} if it is mutable. - */ - public static Map getMutable(Map map) { - if (map instanceof HashMap) { - return map; - } - return new HashMap<>(map); - } -} diff --git a/src/main/java/ru/bclib/util/ColorExtractor.java b/src/main/java/ru/bclib/util/ColorExtractor.java deleted file mode 100644 index 221ea5f5..00000000 --- a/src/main/java/ru/bclib/util/ColorExtractor.java +++ /dev/null @@ -1,149 +0,0 @@ -package ru.bclib.util; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Random;import net.minecraft.util.RandomSource; - -public class ColorExtractor { - private List

centers = new ArrayList<>(); - private List colors; - private Integer result; - - public ColorExtractor(List colors) { - this.colors = colors; - Random rnd = new Random(); - int size = colors.size(); - for (int i = 0; i < 4; i++) { - int color = colors.get(rnd.nextInt(size)); - this.centers.add(new Center(color)); - } - } - - public int analize() { - boolean moved = true; - while (moved) { - this.remap(); - moved = false; - for (Center center : centers) { - if (center.move()) { - moved = true; - } - } - } - List
toClear = new ArrayList<>(); - this.centers.forEach(center -> { - if (center.colors.isEmpty()) { - toClear.add(center); - } - }); - if (toClear.size() > 0) { - toClear.forEach(clear -> centers.remove(clear)); - } - this.centers.sort(Center.COMPARATOR); - - return this.getResult(); - } - - public int getResult() { - if (result == null) { - double weights = 0; - double alpha = 0; - double red = 0; - double green = 0; - double blue = 0; - for (Center center : centers) { - double weight = (double) center.colors.size() / colors.size(); - weights += weight; - alpha += center.a * weight; - red += center.r * weight; - green += center.g * weight; - blue += center.b * weight; - } - ; - - int a = (int) Math.round(alpha / weights); - int r = (int) Math.round(red / weights); - int g = (int) Math.round(green / weights); - int b = (int) Math.round(blue / weights); - - this.result = a << 24 | r << 16 | g << 8 | b; - } - - return this.result; - } - - private void remap() { - this.centers.forEach(entry -> entry.colors.clear()); - this.colors.forEach(color -> { - int id = 0; - int base = centers.get(0).getColor(); - int dst = ColorUtil.colorDistance(color, base); - for (Center center : centers) { - base = center.getColor(); - int dst1 = ColorUtil.colorDistance(color, base); - if (dst1 < dst) { - dst = dst1; - id = centers.indexOf(center); - } - } - this.centers.get(id).colors.add(color); - }); - } - - private static class Center { - static final Comparator
COMPARATOR = new Comparator
() { - @Override - public int compare(Center c1, Center c2) { - return Integer.compare(c1.getColor(), c2.getColor()); - } - }; - - List colors = new ArrayList<>(); - double a, r, g, b; - - Center(int color) { - this.a = (color >> 24) & 255; - this.r = (color >> 16) & 255; - this.g = (color >> 8) & 255; - this.b = color & 255; - } - - private void update(double a, double r, double g, double b) { - this.a = a; - this.r = r; - this.g = g; - this.b = b; - } - - public int getColor() { - int a = (int) Math.round(this.a); - int r = (int) Math.round(this.r); - int g = (int) Math.round(this.g); - int b = (int) Math.round(this.b); - return a << 24 | r << 16 | g << 8 | b; - } - - public boolean move() { - double or = r; - double og = g; - double ob = b; - double a = 0, r = 0, g = 0, b = 0; - int size = this.colors.size(); - for (int col : colors) { - a += (col >> 24) & 255; - r += (col >> 16) & 255; - g += (col >> 8) & 255; - b += col & 255; - } - a /= size; - r /= size; - g /= size; - b /= size; - - this.update(a, r, g, b); - - return Math.abs(r - or) > 0.1 || Math.abs(g - og) > 0.1 || Math.abs(b - ob) > 0.1; - } - } -} diff --git a/src/main/java/ru/bclib/util/ColorUtil.java b/src/main/java/ru/bclib/util/ColorUtil.java deleted file mode 100644 index c6ac762b..00000000 --- a/src/main/java/ru/bclib/util/ColorUtil.java +++ /dev/null @@ -1,248 +0,0 @@ -package ru.bclib.util; - -import com.google.common.collect.Maps; -import com.mojang.blaze3d.platform.NativeImage; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; -import net.minecraft.client.Minecraft; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.util.Mth; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.Item; -import ru.bclib.BCLib; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ColorUtil { - private static final float[] FLOAT_BUFFER = new float[4]; - private static final int ALPHA = 255 << 24; - - public static int color(int r, int g, int b) { - return ALPHA | (r << 16) | (g << 8) | b; - } - - public static int color(String hex) { - int r = Integer.parseInt(hex.substring(0, 2), 16); - int g = Integer.parseInt(hex.substring(2, 4), 16); - int b = Integer.parseInt(hex.substring(4, 6), 16); - return color(r, g, b); - } - - public static int[] toIntArray(int color) { - return new int[] {(color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, color & 255}; - } - - public static float[] toFloatArray(int color) { - FLOAT_BUFFER[0] = ((color >> 16 & 255) / 255.0F); - FLOAT_BUFFER[1] = ((color >> 8 & 255) / 255.0F); - FLOAT_BUFFER[2] = ((color & 255) / 255.0F); - FLOAT_BUFFER[3] = ((color >> 24 & 255) / 255.0F); - - return FLOAT_BUFFER; - } - - public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) { - float hue, saturation, brightness; - if (hsbvals == null) { - hsbvals = FLOAT_BUFFER; - } - int cmax = (r > g) ? r : g; - if (b > cmax) cmax = b; - int cmin = (r < g) ? r : g; - if (b < cmin) cmin = b; - - brightness = ((float) cmax) / 255.0F; - if (cmax != 0) saturation = ((float) (cmax - cmin)) / ((float) cmax); - else saturation = 0; - if (saturation == 0) hue = 0; - else { - float redc = ((float) (cmax - r)) / ((float) (cmax - cmin)); - float greenc = ((float) (cmax - g)) / ((float) (cmax - cmin)); - float bluec = ((float) (cmax - b)) / ((float) (cmax - cmin)); - if (r == cmax) hue = bluec - greenc; - else if (g == cmax) hue = 2.0F + redc - bluec; - else hue = 4.0F + greenc - redc; - hue = hue / 6.0F; - if (hue < 0) hue = hue + 1.0F; - } - hsbvals[0] = hue; - hsbvals[1] = saturation; - hsbvals[2] = brightness; - return hsbvals; - } - - public static int HSBtoRGB(float hue, float saturation, float brightness) { - int r = 0, g = 0, b = 0; - if (saturation == 0) { - r = g = b = (int) (brightness * 255.0F + 0.5F); - } - else { - float h = (hue - (float) Math.floor(hue)) * 6.0F; - float f = h - (float) java.lang.Math.floor(h); - float p = brightness * (1.0F - saturation); - float q = brightness * (1.0F - saturation * f); - float t = brightness * (1.0F - (saturation * (1.0F - f))); - switch ((int) h) { - case 0: - r = (int) (brightness * 255.0F + 0.5F); - g = (int) (t * 255.0F + 0.5F); - b = (int) (p * 255.0F + 0.5F); - break; - case 1: - r = (int) (q * 255.0F + 0.5F); - g = (int) (brightness * 255.0F + 0.5F); - b = (int) (p * 255.0F + 0.5F); - break; - case 2: - r = (int) (p * 255.0F + 0.5F); - g = (int) (brightness * 255.0F + 0.5F); - b = (int) (t * 255.0F + 0.5F); - break; - case 3: - r = (int) (p * 255.0F + 0.5F); - g = (int) (q * 255.0F + 0.5F); - b = (int) (brightness * 255.0F + 0.5F); - break; - case 4: - r = (int) (t * 255.0F + 0.5F); - g = (int) (p * 255.0F + 0.5F); - b = (int) (brightness * 255.0F + 0.5F); - break; - case 5: - r = (int) (brightness * 255.0F + 0.5F); - g = (int) (p * 255.0F + 0.5F); - b = (int) (q * 255.0F + 0.5F); - break; - } - } - return 0xFF000000 | (r << 16) | (g << 8) | (b << 0); - } - - public static int parseHex(String hexColor) { - int len = hexColor.length(); - if (len < 6 || len > 8 || len % 2 > 0) { - return -1; - } - - int color, shift; - if (len == 6) { - color = 0xFF000000; - shift = 16; - } - else { - color = 0; - shift = 24; - } - - try { - String[] splited = hexColor.split("(?<=\\G.{2})"); - for (String digit : splited) { - color |= Integer.valueOf(digit, 16) << shift; - shift -= 8; - } - } - catch (NumberFormatException ex) { - BCLib.LOGGER.catching(ex); - return -1; - } - - return color; - } - - public static int toABGR(int color) { - int r = (color >> 16) & 255; - int g = (color >> 8) & 255; - int b = color & 255; - return 0xFF000000 | b << 16 | g << 8 | r; - } - - public static int ABGRtoARGB(int color) { - int a = (color >> 24) & 255; - int b = (color >> 16) & 255; - int g = (color >> 8) & 255; - int r = color & 255; - return a << 24 | r << 16 | g << 8 | b; - } - - public static int colorBrigtness(int color, float val) { - RGBtoHSB((color >> 16) & 255, (color >> 8) & 255, color & 255, FLOAT_BUFFER); - FLOAT_BUFFER[2] += val / 10.0F; - FLOAT_BUFFER[2] = Mth.clamp(FLOAT_BUFFER[2], 0.0F, 1.0F); - return HSBtoRGB(FLOAT_BUFFER[0], FLOAT_BUFFER[1], FLOAT_BUFFER[2]); - } - - public static int applyTint(int color, int tint) { - return colorBrigtness(ColorHelper.multiplyColor(color, tint), 1.5F); - } - - public static int colorDistance(int color1, int color2) { - int r1 = (color1 >> 16) & 255; - int g1 = (color1 >> 8) & 255; - int b1 = color1 & 255; - int r2 = (color2 >> 16) & 255; - int g2 = (color2 >> 8) & 255; - int b2 = color2 & 255; - return MHelper.sqr(r1 - r2) + MHelper.sqr(g1 - g2) + MHelper.sqr(b1 - b2); - } - - private static Map colorPalette = Maps.newHashMap(); - - @Environment(EnvType.CLIENT) - public static int extractColor(Item item) { - ResourceLocation id = Registry.ITEM.getKey(item); - if (id.equals(Registry.ITEM.getDefaultKey())) return -1; - if (colorPalette.containsKey(id)) { - return colorPalette.get(id); - } - ResourceLocation texture; - if (item instanceof BlockItem) { - texture = new ResourceLocation(id.getNamespace(), "textures/block/" + id.getPath() + ".png"); - } - else { - texture = new ResourceLocation(id.getNamespace(), "textures/item/" + id.getPath() + ".png"); - } - NativeImage image = loadImage(texture, 16, 16); - List colors = new ArrayList<>(); - for (int i = 0; i < image.getWidth(); i++) { - for (int j = 0; j < 16; j++) { - int col = image.getPixelRGBA(i, j); - if (((col >> 24) & 255) > 0) { - colors.add(ABGRtoARGB(col)); - } - } - } - image.close(); - - if (colors.size() == 0) return -1; - - ColorExtractor extractor = new ColorExtractor(colors); - int color = extractor.analize(); - colorPalette.put(id, color); - - return color; - } - - @Environment(EnvType.CLIENT) - public static NativeImage loadImage(ResourceLocation image, int w, int h) { - Minecraft minecraft = Minecraft.getInstance(); - ResourceManager resourceManager = minecraft.getResourceManager(); - var imgResource = resourceManager.getResource(image); - if (imgResource.isPresent()) { - try { - return NativeImage.read(imgResource.get().open()); - } - catch (IOException e) { - BCLib.LOGGER.warning("Can't load texture image: {}. Will be created empty image.", image); - BCLib.LOGGER.warning("Cause: {}.", e.getMessage()); - } - } - return new NativeImage(w, h, false); - } -} \ No newline at end of file diff --git a/src/main/java/ru/bclib/util/ItemUtil.java b/src/main/java/ru/bclib/util/ItemUtil.java deleted file mode 100644 index fd639a3b..00000000 --- a/src/main/java/ru/bclib/util/ItemUtil.java +++ /dev/null @@ -1,74 +0,0 @@ -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; - } -} diff --git a/src/main/java/ru/bclib/util/JsonFactory.java b/src/main/java/ru/bclib/util/JsonFactory.java deleted file mode 100644 index 3845fdea..00000000 --- a/src/main/java/ru/bclib/util/JsonFactory.java +++ /dev/null @@ -1,142 +0,0 @@ -package ru.bclib.util; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; - -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.nio.charset.StandardCharsets; - -public class JsonFactory { - public final static Gson GSON = new GsonBuilder().setPrettyPrinting() - .create(); - - public static JsonObject getJsonObject(InputStream stream) { - try { - Reader reader = new InputStreamReader(stream,StandardCharsets.UTF_8); - JsonElement json = loadJson(reader); - if (json != null && json.isJsonObject()) { - JsonObject jsonObject = json.getAsJsonObject(); - return jsonObject != null ? jsonObject : new JsonObject(); - } - } - catch (Exception ex) { - BCLib.LOGGER.catching(ex); - } - return new JsonObject(); - } - - public static JsonObject getJsonObject(File jsonFile) { - if (jsonFile.exists()) { - JsonElement json = loadJson(jsonFile); - if (json != null && json.isJsonObject()) { - JsonObject jsonObject = json.getAsJsonObject(); - return jsonObject != null ? jsonObject : new JsonObject(); - } - } - return new JsonObject(); - } - - /** - * Loads {@link JsonObject} from resource location using Minecraft resource manager. Can be used to load JSON from resourcepacks and resources. - * - * @param location {@link ResourceLocation} to JSON file - * @return {@link JsonObject} - */ - @Nullable - @Environment(EnvType.CLIENT) - public static JsonObject getJsonObject(ResourceLocation location) { - ResourceManager manager = Minecraft.getInstance() - .getResourceManager(); - JsonObject obj = null; - try { - Resource resource = manager.getResource(location).orElse(null); - if (resource != null) { - InputStream stream = resource.open(); - InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); - obj = JsonFactory.GSON.fromJson(reader, JsonObject.class); - reader.close(); - stream.close(); - } - } - catch (IOException ex) { - } - return obj; - } - - public static JsonElement loadJson(File jsonFile) { - if (jsonFile.exists()) { - try (Reader reader = new FileReader(jsonFile, StandardCharsets.UTF_8)) { - return loadJson(reader); - } - catch (Exception ex) { - BCLib.LOGGER.catching(ex); - } - } - return null; - } - - public static JsonElement loadJson(Reader reader) { - return GSON.fromJson(reader, JsonElement.class); - } - - public static void storeJson(File jsonFile, JsonElement jsonObject) { - try (FileWriter writer = new FileWriter(jsonFile,StandardCharsets.UTF_8)) { - String json = GSON.toJson(jsonObject); - writer.write(json); - writer.flush(); - } - catch (IOException ex) { - BCLib.LOGGER.catching(ex); - } - } - - public static void storeJson(OutputStream outStream, JsonElement jsonObject) { - OutputStreamWriter writer = new OutputStreamWriter(outStream,StandardCharsets.UTF_8); - GSON.toJson(jsonObject, writer); - try { - writer.flush(); - } - catch (IOException e) { - BCLib.LOGGER.error(e.getMessage()); - e.printStackTrace(); - } - } - - public static int getInt(JsonObject object, String member, int def) { - JsonElement elem = object.get(member); - return elem == null ? def : elem.getAsInt(); - } - - public static float getFloat(JsonObject object, String member, float def) { - JsonElement elem = object.get(member); - return elem == null ? def : elem.getAsFloat(); - } - - public static boolean getBoolean(JsonObject object, String member, boolean def) { - JsonElement elem = object.get(member); - return elem == null ? def : elem.getAsBoolean(); - } - - public static String getString(JsonObject object, String member, String def) { - JsonElement elem = object.get(member); - return elem == null ? def : elem.getAsString(); - } -} diff --git a/src/main/java/ru/bclib/util/Logger.java b/src/main/java/ru/bclib/util/Logger.java deleted file mode 100644 index 68cce157..00000000 --- a/src/main/java/ru/bclib/util/Logger.java +++ /dev/null @@ -1,62 +0,0 @@ -package ru.bclib.util; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; - -public final class Logger { - private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(); - private final String modPref; - - public Logger(String modID) { - this.modPref = "[" + modID + "] "; - } - - public void log(Level level, String message) { - LOGGER.log(level, modPref + message); - } - - public void log(Level level, String message, Object... params) { - LOGGER.log(level, modPref + message, params); - } - - public void debug(Object message) { - this.log(Level.DEBUG, message.toString()); - } - - public void debug(Object message, Object... params) { - this.log(Level.DEBUG, message.toString(), params); - } - - public void catching(Throwable ex) { - this.error(ex.getLocalizedMessage()); - LOGGER.catching(ex); - } - - public void info(String message) { - this.log(Level.INFO, message); - } - - public void info(String message, Object... params) { - this.log(Level.INFO, message, params); - } - - public void warning(String message, Object... params) { - this.log(Level.WARN, message, params); - } - - public void warning(String message, Object obj, Exception ex) { - LOGGER.warn(modPref + message, obj, ex); - } - - public void error(String message) { - this.log(Level.ERROR, message); - } - - public void error(String message, Object obj, Exception ex) { - LOGGER.error(modPref + message, obj, ex); - } - - public void error(String message, Exception ex) { - LOGGER.error(modPref + message, ex); - } -} diff --git a/src/main/java/ru/bclib/util/MHelper.java b/src/main/java/ru/bclib/util/MHelper.java deleted file mode 100644 index 732bb987..00000000 --- a/src/main/java/ru/bclib/util/MHelper.java +++ /dev/null @@ -1,224 +0,0 @@ -package ru.bclib.util; - -import com.mojang.math.Vector3f; -import net.minecraft.core.Vec3i; - -import java.util.Random;import net.minecraft.util.RandomSource; -import net.minecraft.world.level.levelgen.LegacyRandomSource; - -public class MHelper { - private static final Vec3i[] RANDOM_OFFSETS = new Vec3i[3 * 3 * 3 - 1]; - private static final float RAD_TO_DEG = 57.295779513082320876798154814105F; - public static final float PHI = (float) (Math.PI * (3 - Math.sqrt(5))); - public static final float PI2 = (float) (Math.PI * 2); - public static final Random RANDOM = new Random(); - public static final RandomSource RANDOM_SOURCE = new LegacyRandomSource(RANDOM.nextLong()); - - public static int randRange(int min, int max, RandomSource random) { - return min + random.nextInt(max - min + 1); - } - - public static double randRange(double min, double max, RandomSource random) { - return min + random.nextDouble() * (max - min); - } - - public static float randRange(float min, float max, RandomSource random) { - return min + random.nextFloat() * (max - min); - } - - public static byte setBit(byte source, int pos, boolean value) { - return value ? setBitTrue(source, pos) : setBitFalse(source, pos); - } - - public static byte setBitTrue(byte source, int pos) { - source |= 1 << pos; - return source; - } - - public static byte setBitFalse(byte source, int pos) { - source &= ~(1 << pos); - return source; - } - - public static boolean getBit(byte source, int pos) { - return ((source >> pos) & 1) == 1; - } - - public static float wrap(float x, float side) { - return x - floor(x / side) * side; - } - - public static int floor(double x) { - return x < 0 ? (int) (x - 1) : (int) x; - } - - public static int min(int a, int b) { - return a < b ? a : b; - } - - public static int min(int a, int b, int c) { - return min(a, min(b, c)); - } - - public static int max(int a, int b) { - return a > b ? a : b; - } - - public static float min(float a, float b) { - return a < b ? a : b; - } - - public static float max(float a, float b) { - return a > b ? a : b; - } - - public static float max(float a, float b, float c) { - return max(a, max(b, c)); - } - - public static int max(int a, int b, int c) { - return max(a, max(b, c)); - } - - public static boolean isEven(int num) { - return (num & 1) == 0; - } - - public static float lengthSqr(float x, float y, float z) { - return x * x + y * y + z * z; - } - - public static double lengthSqr(double x, double y, double z) { - return x * x + y * y + z * z; - } - - public static float length(float x, float y, float z) { - return (float) Math.sqrt(lengthSqr(x, y, z)); - } - - public static double length(double x, double y, double z) { - return Math.sqrt(lengthSqr(x, y, z)); - } - - public static float lengthSqr(float x, float y) { - return x * x + y * y; - } - - public static double lengthSqr(double x, double y) { - return x * x + y * y; - } - - public static float length(float x, float y) { - return (float) Math.sqrt(lengthSqr(x, y)); - } - - public static double length(double x, double y) { - return Math.sqrt(lengthSqr(x, y)); - } - - public static float dot(float x1, float y1, float z1, float x2, float y2, float z2) { - return x1 * x2 + y1 * y2 + z1 * z2; - } - - public static float dot(float x1, float y1, float x2, float y2) { - return x1 * x2 + y1 * y2; - } - - public static int getRandom(int x, int z) { - int h = x * 374761393 + z * 668265263; - h = (h ^ (h >> 13)) * 1274126177; - return h ^ (h >> 16); - } - - public static int getSeed(int seed, int x, int y) { - int h = seed + x * 374761393 + y * 668265263; - h = (h ^ (h >> 13)) * 1274126177; - return h ^ (h >> 16); - } - - public static int getSeed(int seed, int x, int y, int z) { - int h = seed + x * 374761393 + y * 668265263 + z; - h = (h ^ (h >> 13)) * 1274126177; - return h ^ (h >> 16); - } - - public static void shuffle(T[] array, RandomSource random) { - for (int i = 0; i < array.length; i++) { - int i2 = random.nextInt(array.length); - T element = array[i]; - array[i] = array[i2]; - array[i2] = element; - } - } - - public static int sqr(int i) { - return i * i; - } - - public static float sqr(float f) { - return f * f; - } - - public static double sqr(double d) { - return d * d; - } - - public static final float radiansToDegrees(float value) { - return value * RAD_TO_DEG; - } - - public static final float degreesToRadians(float value) { - return value / RAD_TO_DEG; - } - - public static Vector3f cross(Vector3f vec1, Vector3f vec2) { - float cx = vec1.y() * vec2.z() - vec1.z() * vec2.y(); - float cy = vec1.z() * vec2.x() - vec1.x() * vec2.z(); - float cz = vec1.x() * vec2.y() - vec1.y() * vec2.x(); - return new Vector3f(cx, cy, cz); - } - - public static Vector3f normalize(Vector3f vec) { - float length = lengthSqr(vec.x(), vec.y(), vec.z()); - if (length > 0) { - length = (float) Math.sqrt(length); - float x = vec.x() / length; - float y = vec.y() / length; - float z = vec.z() / length; - vec.set(x, y, z); - } - return vec; - } - - public static float angle(Vector3f vec1, Vector3f vec2) { - float dot = vec1.x() * vec2.x() + vec1.y() * vec2.y() + vec1.z() * vec2.z(); - float length1 = lengthSqr(vec1.x(), vec1.y(), vec1.z()); - float length2 = lengthSqr(vec2.x(), vec2.y(), vec2.z()); - return (float) Math.acos(dot / Math.sqrt(length1 * length2)); - } - - public static Vector3f randomHorizontal(RandomSource random) { - float angleY = randRange(0, PI2, random); - float vx = (float) Math.sin(angleY); - float vz = (float) Math.cos(angleY); - return new Vector3f(vx, 0, vz); - } - - public static Vec3i[] getOffsets(RandomSource random) { - shuffle(RANDOM_OFFSETS, random); - return RANDOM_OFFSETS; - } - - static { - int index = 0; - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - if (x != 0 || y != 0 || z != 0) { - RANDOM_OFFSETS[index++] = new Vec3i(x, y, z); - } - } - } - } - } -} diff --git a/src/main/java/ru/bclib/util/MethodReplace.java b/src/main/java/ru/bclib/util/MethodReplace.java deleted file mode 100644 index 5816d76b..00000000 --- a/src/main/java/ru/bclib/util/MethodReplace.java +++ /dev/null @@ -1,46 +0,0 @@ -package ru.bclib.util; - -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Function; - -public class MethodReplace { - private static Function itemReplace; - private static Function blockReplace; - private static Block block; - private static Item item; - - public static void addItemReplace(Item item, Function itemReplace) { - MethodReplace.itemReplace = itemReplace; - MethodReplace.item = item; - } - - public static void addBlockReplace(Block block, Function blockReplace) { - MethodReplace.blockReplace = blockReplace; - MethodReplace.block = block; - } - - @Nullable - public static Function getItemReplace(Item item) { - if (MethodReplace.item != item) { - return null; - } - Function replace = itemReplace; - itemReplace = null; - return replace; - } - - @Nullable - public static Function getBlockReplace(Block block) { - if (MethodReplace.block != block) { - return null; - } - Function replace = blockReplace; - blockReplace = null; - return replace; - } -} diff --git a/src/main/java/ru/bclib/util/ModUtil.java b/src/main/java/ru/bclib/util/ModUtil.java deleted file mode 100644 index 7c1d2d40..00000000 --- a/src/main/java/ru/bclib/util/ModUtil.java +++ /dev/null @@ -1,456 +0,0 @@ -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.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.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ModUtil { - private static Map mods; - - /** - * Unloads the cache of available mods created from {@link #getMods()} - */ - public static void invalidateCachedMods() { - mods = null; - } - - /** - * return a map of all mods that were found in the 'mods'-folder. - *

- * The method will cache the results. You can clear that cache (and free the memory) by - * calling {@link #invalidateCachedMods()} - *

- * An error message is printed if a mod fails to load, but the parsing will continue. - * - * @return A map of all found mods. (key=ModID, value={@link ModInfo}) - */ - public static Map getMods() { - if (mods != null) return mods; - - mods = new HashMap<>(); - org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("BCLib|ModLoader"); - PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (ModUtil::accept)); - - 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 getProvides() { - return new ArrayList<>(); - } - - @Override - public ModEnvironment getEnvironment() { - JsonElement env = data.get("environment"); - if (env == null) { - BCLib.LOGGER.warning("No environment specified in " + sourceFile); - //return ModEnvironment.UNIVERSAL; - } - final String environment = env == null ? "" : env.getAsString() - .toLowerCase(Locale.ROOT); - - if (environment.isEmpty() || environment.equals("*") || 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 getDepends() { - return new ArrayList<>(); - } - - @Override - public Collection getRecommends() { - return new ArrayList<>(); - } - - @Override - public Collection getSuggests() { - return new ArrayList<>(); - } - - @Override - public Collection getConflicts() { - return new ArrayList<>(); - } - - @Override - public Collection getBreaks() { - return new ArrayList<>(); - } - - public Collection getDependencies() { - return new ArrayList<>(); - } - - @Override - public String getName() { - return data.get("name") - .getAsString(); - } - - @Override - public String getDescription() { - return ""; - } - - @Override - public Collection getAuthors() { - return new ArrayList<>(); - } - - @Override - public Collection getContributors() { - return new ArrayList<>(); - } - - @Override - public ContactInformation getContact() { - return null; - } - - @Override - public Collection getLicense() { - return new ArrayList<>(); - } - - @Override - public Optional 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 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. - *

- * The call will also return null if the mode-Version in the jar-File is not the same - * as the version of the loaded Mod. - * - * @param modID The mod ID to query - * @return A {@link ModInfo}-Object for the querried Mod. - */ - public static ModInfo getModInfo(String modID) { - return getModInfo(modID, true); - } - - public static ModInfo getModInfo(String modID, boolean matchVersion) { - getMods(); - final ModInfo mi = mods.get(modID); - if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null; - return mi; - } - - /** - * Local Mod Version for the queried Mod - * - * @param modID The mod ID to query - * @return The version of the locally installed Mod - */ - public static String getModVersion(String modID) { - Optional optional = FabricLoader.getInstance() - .getModContainer(modID); - if (optional.isPresent()) { - ModContainer modContainer = optional.get(); - return ModInfo.versionToString(modContainer.getMetadata() - .getVersion()); - - } - - return getModVersionFromJar(modID); - } - - /** - * Local Mod Version for the queried Mod from the Jar-File in the games mod-directory - * - * @param modID The mod ID to query - * @return The version of the locally installed Mod - */ - public static String getModVersionFromJar(String modID) { - final ModInfo mi = getModInfo(modID, false); - if (mi != null) return mi.getVersion(); - - return "0.0.0"; - } - - /** - * Get mod version from string. String should be in format: %d.%d.%d - * - * @param version - {@link String} mod version. - * @return int mod version. - */ - public static int convertModVersion(String version) { - if (version.isEmpty()) { - return 0; - } - try { - int res = 0; - final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*"; - final Matcher matcher = Pattern.compile(semanticVersionPattern) - .matcher(version); - if (matcher.find()) { - if (matcher.groupCount() > 0) - res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22); - if (matcher.groupCount() > 1) - res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14); - if (matcher.groupCount() > 3) - res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF; - } - - return res; - } - catch (Exception e) { - return 0; - } - } - - /** - * Get mod version from integer. String will be in format %d.%d.%d - * - * @param version - mod version in integer form. - * @return {@link String} mod version. - */ - public static String convertModVersion(int version) { - int a = (version >> 22) & 0xFF; - int b = (version >> 14) & 0xFF; - int c = version & 0x3FFF; - return String.format(Locale.ROOT, "%d.%d.%d", a, b, c); - } - - /** - * {@code true} if the version v1 is larger than v2 - * - * @param v1 A Version string - * @param v2 Another Version string - * @return v1 > v2 - */ - public static boolean isLargerVersion(String v1, String v2) { - return convertModVersion(v1) > convertModVersion(v2); - } - - /** - * {@code true} if the version v1 is larger or equal v2 - * - * @param v1 A Version string - * @param v2 Another Version string - * @return v1 ≥ v2 - */ - public static boolean isLargerOrEqualVersion(String v1, String v2) { - return convertModVersion(v1) >= convertModVersion(v2); - } - - private static void accept(Path file) { - try { - URI uri = URI.create("jar:" + file.toUri()); - - FileSystem fs; - // boolean doClose = false; - try { - fs = FileSystems.getFileSystem(uri); - } - catch (Exception e) { - // doClose = true; - fs = FileSystems.newFileSystem(file); - } - if (fs != null) { - try { - 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()); - ModMetadata mc = readJSON(is, uri.toString()); - if (mc != null) { - mods.put(mc.getId(), new ModInfo(mc, file)); - } - } - } - } - catch (Exception e) { - BCLib.LOGGER.error("Error for " + uri + ": " + e.toString()); - } - //if (doClose) fs.close(); - } - } - catch (Exception e) { - BCLib.LOGGER.error("Error for " + file.toUri() + ": " + e.toString()); - e.printStackTrace(); - } - } - - public static class ModInfo { - public final ModMetadata metadata; - public final Path jarPath; - - ModInfo(ModMetadata metadata, Path jarPath) { - this.metadata = metadata; - this.jarPath = jarPath; - } - - public static String versionToString(Version v) { - if (v instanceof SemanticVersion) { - return versionToString((SemanticVersion) v); - } - return convertModVersion(convertModVersion(v.toString())); - } - - public static String versionToString(SemanticVersion v) { - StringBuilder stringBuilder = new StringBuilder(); - boolean first = true; - final int cCount = Math.min(v.getVersionComponentCount(), 3); - for (int i = 0; i < cCount; i++) { - if (first) { - first = false; - } - else { - stringBuilder.append('.'); - } - - stringBuilder.append(v.getVersionComponent(i)); - } - - return stringBuilder.toString(); - } - - @Override - public String toString() { - return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}'; - } - - public String getVersion() { - if (metadata == null) { - return "0.0.0"; - } - return versionToString(metadata.getVersion()); - } - } -} diff --git a/src/main/java/ru/bclib/util/Pair.java b/src/main/java/ru/bclib/util/Pair.java deleted file mode 100644 index c542ac09..00000000 --- a/src/main/java/ru/bclib/util/Pair.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.bclib.util; - -import java.util.Objects; - -public class Pair { - public final A first; - public final B second; - - public Pair(A first, B second) { - this.first = first; - this.second = second; - } - - @Override - public String toString() { - return "Pair{" + "first=" + first + ", second=" + second + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Pair)) return false; - Pair pair = (Pair) o; - return Objects.equals(first, pair.first) && Objects.equals(second, pair.second); - } - - @Override - public int hashCode() { - return Objects.hash(first, second); - } -} diff --git a/src/main/java/ru/bclib/util/PathUtil.java b/src/main/java/ru/bclib/util/PathUtil.java deleted file mode 100644 index e66b98ba..00000000 --- a/src/main/java/ru/bclib/util/PathUtil.java +++ /dev/null @@ -1,101 +0,0 @@ -package ru.bclib.util; - -import net.fabricmc.loader.api.FabricLoader; - -import java.io.File; -import java.nio.file.Path; -import java.util.function.Consumer; - -public class PathUtil { - public final static Path GAME_FOLDER = FabricLoader.getInstance() - .getGameDir() - .normalize(); - - public final static Path MOD_FOLDER = FabricLoader.getInstance() - .getGameDir() - .resolve("mods") - .normalize(); - - public final static Path MOD_BAK_FOLDER = MOD_FOLDER.resolve("_bclib_deactivated") - .normalize(); - - /** - * Tests if a path is a child-path. - *

- * A path is a child of another if it is located in the parent or any of the parents subdirectories - * - * @param parent The folder we search for the {@code child} - * @param child The folder you want to test - * @return {@code true} if {@code child} is in {@code parent} or any of its sub directories - */ - public static boolean isChildOf(Path parent, Path child) { - if (child == null || parent == null) return false; - parent = parent.toAbsolutePath().normalize(); - child = child.toAbsolutePath().normalize(); - - final int pCount = parent.getNameCount(); - final int cCount = child.getNameCount(); - - if (cCount > pCount) return isChildOf(parent, child.getParent()); - if (cCount < pCount) return false; - - return child.equals(parent); - } - - /** - * A simple directory walker that ignores dot-files - * - * @param path The path where you want to start - * @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object - * for each visited file - */ - public static void fileWalker(File path, Consumer pathConsumer) { - fileWalker(path, true, pathConsumer); - } - - /** - * A simple directory walker that ignores dot-files - * - * @param path The path where you want to start - * @param recursive if {@code false}, only the {@code path} is traversed - * @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object - * for each visited file - */ - public static void fileWalker(File path, boolean recursive, Consumer pathConsumer) { - if (!path.exists()) return; - for (final File f : path.listFiles()) { - if (f.getName() - .startsWith(".")) continue; - if (f.isDirectory()) { - if (recursive) fileWalker(f, pathConsumer); - } - else if (f.isFile()) { - pathConsumer.accept(f.toPath()); - } - } - } - - /** - * Creates a human readable File-Size - * - * @param size Filesize in bytes - * @return A Human readable String - */ - public static String humanReadableFileSize(long size) { - final int threshold = 2; - final int factor = 1024; - if (size < 0) return "? Byte"; - if (size < factor * threshold) { - return size + " Byte"; - } - char[] units = {'K', 'M', 'G', 'T', 'P'}; - int unitIndex = 0; - double fSize = size; - do { - unitIndex++; - fSize /= 1024; - } while (fSize > factor * threshold && unitIndex < units.length); - - return String.format("%.1f %ciB", fSize, units[unitIndex - 1]); - } -} diff --git a/src/main/java/ru/bclib/util/RecipeHelper.java b/src/main/java/ru/bclib/util/RecipeHelper.java deleted file mode 100644 index 82527011..00000000 --- a/src/main/java/ru/bclib/util/RecipeHelper.java +++ /dev/null @@ -1,25 +0,0 @@ -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; - } -} diff --git a/src/main/java/ru/bclib/util/SplineHelper.java b/src/main/java/ru/bclib/util/SplineHelper.java deleted file mode 100644 index 680dac72..00000000 --- a/src/main/java/ru/bclib/util/SplineHelper.java +++ /dev/null @@ -1,341 +0,0 @@ -package ru.bclib.util; - -import com.google.common.collect.Lists; -import com.mojang.math.Vector3f; -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.util.Mth; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.state.BlockState; -import ru.bclib.sdf.SDF; -import ru.bclib.sdf.operator.SDFUnion; -import ru.bclib.sdf.primitive.SDFLine; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random;import net.minecraft.util.RandomSource; -import java.util.function.Function; - -public class SplineHelper { - public static List makeSpline(float x1, float y1, float z1, float x2, float y2, float z2, int points) { - List spline = Lists.newArrayList(); - spline.add(new Vector3f(x1, y1, z1)); - int count = points - 1; - for (int i = 1; i < count; i++) { - float delta = (float) i / (float) count; - float x = Mth.lerp(delta, x1, x2); - float y = Mth.lerp(delta, y1, y2); - float z = Mth.lerp(delta, z1, z2); - spline.add(new Vector3f(x, y, z)); - } - spline.add(new Vector3f(x2, y2, z2)); - return spline; - } - - public static List smoothSpline(List spline, int segmentPoints) { - List result = Lists.newArrayList(); - Vector3f start = spline.get(0); - for (int i = 1; i < spline.size(); i++) { - Vector3f end = spline.get(i); - for (int j = 0; j < segmentPoints; j++) { - float delta = (float) j / segmentPoints; - delta = 0.5F - 0.5F * Mth.cos(delta * 3.14159F); - result.add(lerp(start, end, delta)); - } - start = end; - } - result.add(start); - return result; - } - - private static Vector3f lerp(Vector3f start, Vector3f end, float delta) { - float x = Mth.lerp(delta, start.x(), end.x()); - float y = Mth.lerp(delta, start.y(), end.y()); - float z = Mth.lerp(delta, start.z(), end.z()); - return new Vector3f(x, y, z); - } - - public static void offsetParts(List spline, RandomSource random, float dx, float dy, float dz) { - int count = spline.size(); - for (int i = 1; i < count; i++) { - Vector3f pos = spline.get(i); - float x = pos.x() + (float) random.nextGaussian() * dx; - float y = pos.y() + (float) random.nextGaussian() * dy; - float z = pos.z() + (float) random.nextGaussian() * dz; - pos.set(x, y, z); - } - } - - public static void powerOffset(List spline, float distance, float power) { - int count = spline.size(); - float max = count + 1; - for (int i = 1; i < count; i++) { - Vector3f pos = spline.get(i); - float x = (float) i / max; - float y = pos.y() + (float) Math.pow(x, power) * distance; - pos.set(pos.x(), y, pos.z()); - } - } - - public static SDF buildSDF(List spline, float radius1, float radius2, Function placerFunction) { - int count = spline.size(); - float max = count - 2; - SDF result = null; - Vector3f start = spline.get(0); - for (int i = 1; i < count; i++) { - Vector3f pos = spline.get(i); - float delta = (float) (i - 1) / max; - SDF line = new SDFLine().setRadius(Mth.lerp(delta, radius1, radius2)) - .setStart(start.x(), start.y(), start.z()) - .setEnd(pos.x(), pos.y(), pos.z()) - .setBlock(placerFunction); - result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line); - start = pos; - } - return result; - } - - public static SDF buildSDF(List spline, Function radiusFunction, Function placerFunction) { - int count = spline.size(); - float max = count - 2; - SDF result = null; - Vector3f start = spline.get(0); - for (int i = 1; i < count; i++) { - Vector3f pos = spline.get(i); - float delta = (float) (i - 1) / max; - SDF line = new SDFLine().setRadius(radiusFunction.apply(delta)) - .setStart(start.x(), start.y(), start.z()) - .setEnd(pos.x(), pos.y(), pos.z()) - .setBlock(placerFunction); - result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line); - start = pos; - } - return result; - } - - public static boolean fillSpline(List spline, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { - Vector3f startPos = spline.get(0); - for (int i = 1; i < spline.size(); i++) { - Vector3f endPos = spline.get(i); - if (!(fillLine(startPos, endPos, world, state, pos, replace))) { - return false; - } - startPos = endPos; - } - - return true; - } - - public static void fillSplineForce(List spline, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { - Vector3f startPos = spline.get(0); - for (int i = 1; i < spline.size(); i++) { - Vector3f endPos = spline.get(i); - fillLineForce(startPos, endPos, world, state, pos, replace); - startPos = endPos; - } - } - - public static boolean fillLine(Vector3f start, Vector3f end, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { - float dx = end.x() - start.x(); - float dy = end.y() - start.y(); - float dz = end.z() - start.z(); - float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); - int count = MHelper.floor(max + 1); - dx /= max; - dy /= max; - dz /= max; - float x = start.x(); - float y = start.y(); - float z = start.z(); - boolean down = Math.abs(dy) > 0.2; - - BlockState bState; - MutableBlockPos bPos = new MutableBlockPos(); - for (int i = 0; i < count; i++) { - bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); - bState = world.getBlockState(bPos); - if (bState.equals(state) || replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - bPos.setY(bPos.getY() - 1); - bState = world.getBlockState(bPos); - if (down && bState.equals(state) || replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - } - } - else { - return false; - } - x += dx; - y += dy; - z += dz; - } - bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ()); - bState = world.getBlockState(bPos); - if (bState.equals(state) || replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - bPos.setY(bPos.getY() - 1); - bState = world.getBlockState(bPos); - if (down && bState.equals(state) || replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - } - return true; - } - else { - return false; - } - } - - public static void fillLineForce(Vector3f start, Vector3f end, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { - float dx = end.x() - start.x(); - float dy = end.y() - start.y(); - float dz = end.z() - start.z(); - float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); - int count = MHelper.floor(max + 1); - dx /= max; - dy /= max; - dz /= max; - float x = start.x(); - float y = start.y(); - float z = start.z(); - boolean down = Math.abs(dy) > 0.2; - - BlockState bState; - MutableBlockPos bPos = new MutableBlockPos(); - for (int i = 0; i < count; i++) { - bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); - bState = world.getBlockState(bPos); - if (replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - bPos.setY(bPos.getY() - 1); - bState = world.getBlockState(bPos); - if (down && replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - } - } - x += dx; - y += dy; - z += dz; - } - bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ()); - bState = world.getBlockState(bPos); - if (replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - bPos.setY(bPos.getY() - 1); - bState = world.getBlockState(bPos); - if (down && replace.apply(bState)) { - BlocksHelper.setWithoutUpdate(world, bPos, state); - } - } - } - - public static boolean canGenerate(List spline, float scale, BlockPos start, WorldGenLevel world, Function canReplace) { - int count = spline.size(); - Vector3f vec = spline.get(0); - MutableBlockPos mut = new MutableBlockPos(); - float x1 = start.getX() + vec.x() * scale; - float y1 = start.getY() + vec.y() * scale; - float z1 = start.getZ() + vec.z() * scale; - for (int i = 1; i < count; i++) { - vec = spline.get(i); - float x2 = start.getX() + vec.x() * scale; - float y2 = start.getY() + vec.y() * scale; - float z2 = start.getZ() + vec.z() * scale; - - for (float py = y1; py < y2; py += 3) { - if (py - start.getY() < 10) continue; - float lerp = (py - y1) / (y2 - y1); - float x = Mth.lerp(lerp, x1, x2); - float z = Mth.lerp(lerp, z1, z2); - mut.set(x, py, z); - if (!canReplace.apply(world.getBlockState(mut))) { - return false; - } - } - - x1 = x2; - y1 = y2; - z1 = z2; - } - return true; - } - - public static boolean canGenerate(List spline, BlockPos start, WorldGenLevel world, Function canReplace) { - int count = spline.size(); - Vector3f vec = spline.get(0); - MutableBlockPos mut = new MutableBlockPos(); - float x1 = start.getX() + vec.x(); - float y1 = start.getY() + vec.y(); - float z1 = start.getZ() + vec.z(); - for (int i = 1; i < count; i++) { - vec = spline.get(i); - float x2 = start.getX() + vec.x(); - float y2 = start.getY() + vec.y(); - float z2 = start.getZ() + vec.z(); - - for (float py = y1; py < y2; py += 3) { - if (py - start.getY() < 10) continue; - float lerp = (py - y1) / (y2 - y1); - float x = Mth.lerp(lerp, x1, x2); - float z = Mth.lerp(lerp, z1, z2); - mut.set(x, py, z); - if (!canReplace.apply(world.getBlockState(mut))) { - return false; - } - } - - x1 = x2; - y1 = y2; - z1 = z2; - } - return true; - } - - public static Vector3f getPos(List spline, float index) { - int i = (int) index; - int last = spline.size() - 1; - if (i >= last) { - return spline.get(last); - } - float delta = index - i; - Vector3f p1 = spline.get(i); - Vector3f p2 = spline.get(i + 1); - float x = Mth.lerp(delta, p1.x(), p2.x()); - float y = Mth.lerp(delta, p1.y(), p2.y()); - float z = Mth.lerp(delta, p1.z(), p2.z()); - return new Vector3f(x, y, z); - } - - public static void rotateSpline(List spline, float angle) { - for (Vector3f v : spline) { - float sin = (float) Math.sin(angle); - float cos = (float) Math.cos(angle); - float x = v.x() * cos + v.z() * sin; - float z = v.x() * sin + v.z() * cos; - v.set(x, v.y(), z); - } - } - - public static List copySpline(List spline) { - List result = new ArrayList(spline.size()); - for (Vector3f v : spline) { - result.add(new Vector3f(v.x(), v.y(), v.z())); - } - return result; - } - - public static void scale(List spline, float scale) { - scale(spline, scale, scale, scale); - } - - public static void scale(List spline, float x, float y, float z) { - for (Vector3f v : spline) { - v.set(v.x() * x, v.y() * y, v.z() * z); - } - } - - public static void offset(List spline, Vector3f offset) { - for (Vector3f v : spline) { - v.set(offset.x() + v.x(), offset.y() + v.y(), offset.z() + v.z()); - } - } -} diff --git a/src/main/java/ru/bclib/util/StructureHelper.java b/src/main/java/ru/bclib/util/StructureHelper.java deleted file mode 100644 index 46df9338..00000000 --- a/src/main/java/ru/bclib/util/StructureHelper.java +++ /dev/null @@ -1,126 +0,0 @@ -package ru.bclib.util; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Vec3i; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.phys.Vec3; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Enumeration; -import java.util.Random;import net.minecraft.util.RandomSource; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -public class StructureHelper { - public static StructureTemplate readStructure(ResourceLocation resource) { - String ns = resource.getNamespace(); - String nm = resource.getPath(); - return readStructure("/data/" + ns + "/structures/" + nm + ".nbt"); - } - - public static StructureTemplate readStructure(File datapack, String path) { - if (datapack.isDirectory()) { - return readStructure(datapack.toString() + "/" + path); - } - else if (datapack.isFile() && datapack.getName().endsWith(".zip")) { - try { - ZipFile zipFile = new ZipFile(datapack); - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - String name = entry.getName(); - long compressedSize = entry.getCompressedSize(); - long normalSize = entry.getSize(); - String type = entry.isDirectory() ? "DIR" : "FILE"; - - System.out.println(name); - System.out.format("\t %s - %d - %d\n", type, compressedSize, normalSize); - } - zipFile.close(); - } - catch (IOException e) { - e.printStackTrace(); - } - } - return null; - } - - public static StructureTemplate readStructure(String path) { - try { - InputStream inputstream = StructureHelper.class.getResourceAsStream(path); - return readStructureFromStream(inputstream); - } - catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException { - CompoundTag nbttagcompound = NbtIo.readCompressed(stream); - - StructureTemplate template = new StructureTemplate(); - template.load(nbttagcompound); - - return template; - } - - public static BlockPos offsetPos(BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror) { - Vec3 offset = StructureTemplate.transform( - Vec3.atCenterOf(structure.getSize()), - mirror, - rotation, - BlockPos.ZERO - ); - return pos.offset(-offset.x * 0.5, 0, -offset.z * 0.5); - } - - public static void placeCenteredBottom(WorldGenLevel world, BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror, RandomSource random) { - placeCenteredBottom(world, pos, structure, rotation, mirror, makeBox(pos), random); - } - - public static void placeCenteredBottom(WorldGenLevel world, BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror, BoundingBox bounds, RandomSource random) { - BlockPos offset = offsetPos(pos, structure, rotation, mirror); - StructurePlaceSettings placementData = new StructurePlaceSettings().setRotation(rotation) - .setMirror(mirror) - .setBoundingBox(bounds); - structure.placeInWorld(world, offset, offset, placementData, random, 4); - } - - private static BoundingBox makeBox(BlockPos pos) { - int sx = ((pos.getX() >> 4) << 4) - 16; - int sz = ((pos.getZ() >> 4) << 4) - 16; - int ex = sx + 47; - int ez = sz + 47; - return BoundingBox.fromCorners(new Vec3i(sx, 0, sz), new Vec3i(ex, 255, ez)); - } - - public static BoundingBox getStructureBounds(BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror) { - Vec3i max = structure.getSize(); - Vec3 min = StructureTemplate.transform(Vec3.atCenterOf(structure.getSize()), mirror, rotation, BlockPos.ZERO); - max = max.offset(-min.x, -min.y, -min.z); - return BoundingBox.fromCorners(pos.offset(min.x, min.y, min.z), max.offset(pos)); - } - - public static BoundingBox intersectBoxes(BoundingBox box1, BoundingBox box2) { - int x1 = MHelper.max(box1.minX(), box2.minX()); - int y1 = MHelper.max(box1.minY(), box2.minY()); - int z1 = MHelper.max(box1.minZ(), box2.minZ()); - - int x2 = MHelper.min(box1.maxX(), box2.maxX()); - int y2 = MHelper.min(box1.maxY(), box2.maxY()); - int z2 = MHelper.min(box1.maxZ(), box2.maxZ()); - - return BoundingBox.fromCorners(new Vec3i(x1, y1, z1), new Vec3i(x2, y2, z2)); - } -} diff --git a/src/main/java/ru/bclib/util/TranslationHelper.java b/src/main/java/ru/bclib/util/TranslationHelper.java deleted file mode 100644 index 59116a59..00000000 --- a/src/main/java/ru/bclib/util/TranslationHelper.java +++ /dev/null @@ -1,119 +0,0 @@ -package ru.bclib.util; - -import com.google.common.collect.Sets; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import net.minecraft.core.Registry; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.resources.ResourceLocation; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Set; - -public class TranslationHelper { - /** - * Print English translation file lines. Translation is "auto-beautified" text (example "strange_thing" -> "Strange Thing"). - * @param modID {@link String} mod ID string. - */ - public static void printMissingEnNames(String modID) { - printMissingNames(modID, "en_us"); - } - - /** - * Prints translation file lines for specified language. - * @param modID {@link String} mod ID string; - * @param languageCode {@link String} language code (example "en_us", "ru_ru"). - */ - public static void printMissingNames(String modID, String languageCode) { - Set missingNames = Sets.newHashSet(); - - Gson gson = new Gson(); - InputStream inputStream = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/" + languageCode + ".json"); - 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)) { - String name = block.getName().getString(); - if (!translation.has(name)) { - missingNames.add(name); - } - } - }); - - Registry.ITEM.forEach(item -> { - if (Registry.ITEM.getKey(item).getNamespace().equals(modID)) { - String name = item.getDescription().getString(); - if (!translation.has(name)) { - missingNames.add(name); - } - } - }); - - BuiltinRegistries.BIOME.forEach(biome -> { - ResourceLocation id = BuiltinRegistries.BIOME.getKey(biome); - if (id.getNamespace().equals(modID)) { - String name = "biome." + modID + "." + id.getPath(); - if (!translation.has(name)) { - missingNames.add(name); - } - } - }); - - Registry.ENTITY_TYPE.forEach((entity) -> { - ResourceLocation id = Registry.ENTITY_TYPE.getKey(entity); - if (id.getNamespace().equals(modID)) { - String name = "entity." + modID + "." + id.getPath(); - if (!translation.has(name)) { - missingNames.add(name); - } - } - }); - - if (!missingNames.isEmpty()) { - - System.out.println("========================================"); - System.out.println(" MISSING NAMES LIST"); - - if (!missingNames.isEmpty()) { - if (languageCode.equals("en_us")) { - System.out.println("========================================"); - System.out.println(" AUTO ENGLISH BEAUTIFICATION"); - System.out.println("========================================"); - missingNames.stream().sorted().forEach(name -> { - System.out.println(" \"" + name + "\": \"" + fastTranslateEn(name) + "\","); - }); - } - else { - System.out.println("========================================"); - System.out.println(" TEMPLATE: [" + languageCode + "]"); - System.out.println("========================================"); - missingNames.stream().sorted().forEach(name -> { - System.out.println(" \"" + name + "\": \"\","); - }); - } - } - - System.out.println("========================================"); - } - } - - /** - * Simple fast text beautification (example "strange_thing" -> "Strange Thing"). - * @param text {@link String} to process; - * @return {@link String} result. - */ - public static String fastTranslateEn(String text) { - String[] words = text.substring(text.lastIndexOf('.') + 1).split("_"); - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < words.length; i++) { - String word = words[i]; - builder.append(Character.toUpperCase(word.charAt(0))); - builder.append(word, 1, word.length()); - if (i < words.length - 1) { - builder.append(' '); - } - } - return builder.toString(); - } -} diff --git a/src/main/java/ru/bclib/util/Triple.java b/src/main/java/ru/bclib/util/Triple.java deleted file mode 100644 index a40f1a90..00000000 --- a/src/main/java/ru/bclib/util/Triple.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.bclib.util; - -import java.util.Objects; - -public class Triple extends Pair { - public final C third; - - public Triple(A first, B second, C third) { - super(first, second); - this.third = third; - } - - @Override - public String toString() { - return "Triple{" + "first=" + first + ", second=" + second + ", third=" + third + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Triple)) return false; - if (!super.equals(o)) return false; - Triple triple = (Triple) o; - return Objects.equals(third, triple.third); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), third); - } -} diff --git a/src/main/java/ru/bclib/util/WeighTree.java b/src/main/java/ru/bclib/util/WeighTree.java deleted file mode 100644 index 130a36cd..00000000 --- a/src/main/java/ru/bclib/util/WeighTree.java +++ /dev/null @@ -1,92 +0,0 @@ -package ru.bclib.util; - -import java.util.Locale; -import java.util.Random;import net.minecraft.util.RandomSource; -import net.minecraft.world.level.levelgen.WorldgenRandom; - -public class WeighTree { - private final float maxWeight; - private final Node root; - - public WeighTree(WeightedList list) { - maxWeight = list.getMaxWeight(); - root = getNode(list); - } - - /** - * Get eandom value from tree. - * - * @param random - {@link Random}. - * @return {@link T} value. - */ - public T get(WorldgenRandom random) { - return root.get(random.nextFloat() * maxWeight); - } - - private Node getNode(WeightedList list) { - int size = list.size(); - if (size == 1) { - return new Leaf(list.get(0)); - } - else if (size == 2) { - T first = list.get(0); - return new Branch(list.getWeight(0), new Leaf(first), new Leaf(list.get(1))); - } - else { - int index = size >> 1; - float separator = list.getWeight(index); - Node a = getNode(list.subList(0, index + 1)); - Node b = getNode(list.subList(index, size)); - return new Branch(separator, a, b); - } - } - - private abstract class Node { - abstract T get(float value); - } - - private class Branch extends Node { - final float separator; - final Node min; - final Node max; - - public Branch(float separator, Node min, Node max) { - this.separator = separator; - this.min = min; - this.max = max; - } - - @Override - T get(float value) { - return value < separator ? min.get(value) : max.get(value); - } - - @Override - public String toString() { - return String.format(Locale.ROOT, "[%f, %s, %s]", separator, min.toString(), max.toString()); - } - } - - private class Leaf extends Node { - final T biome; - - Leaf(T value) { - this.biome = value; - } - - @Override - T get(float value) { - return biome; - } - - @Override - public String toString() { - return String.format(Locale.ROOT, "[%s]", biome.toString()); - } - } - - @Override - public String toString() { - return root.toString(); - } -} diff --git a/src/main/java/ru/bclib/util/WeightedList.java b/src/main/java/ru/bclib/util/WeightedList.java deleted file mode 100644 index 20337c30..00000000 --- a/src/main/java/ru/bclib/util/WeightedList.java +++ /dev/null @@ -1,127 +0,0 @@ -package ru.bclib.util; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random;import net.minecraft.util.RandomSource; -import net.minecraft.world.level.levelgen.WorldgenRandom; - -import java.util.function.Consumer; - -public class WeightedList { - private final List weights = new ArrayList(); - private final List values = new ArrayList(); - private float maxWeight; - - /** - * Adds value with specified weight to the list - * - * @param value - * @param weight - */ - public void add(T value, float weight) { - maxWeight += weight; - weights.add(maxWeight); - values.add(value); - } - - /** - * Get random value. - * - * @param random - {@link Random}. - * @return {@link T} value. - */ - public T get(RandomSource random) { - if (maxWeight < 1) { - return null; - } - float weight = random.nextFloat() * maxWeight; - for (int i = 0; i < weights.size(); i++) { - if (weight <= weights.get(i)) { - return values.get(i); - } - } - return null; - } - - /** - * Get value by index. - * - * @param index - {@code int} index. - * @return {@link T} value. - */ - public T get(int index) { - return values.get(index); - } - - /** - * Get value weight. Weight is summed with all previous values weights. - * - * @param index - {@code int} index. - * @return {@code float} weight. - */ - public float getWeight(int index) { - return weights.get(index); - } - - /** - * Chech if the list is empty. - * - * @return {@code true} if list is empty and {@code false} if not. - */ - public boolean isEmpty() { - return maxWeight == 0; - } - - /** - * Get the list size. - * - * @return {@code int} list size. - */ - public int size() { - return values.size(); - } - - /** - * Makes a sublist of this list with same weights. Used only in {@link WeighTree} - * - * @param start - {@code int} start index (inclusive). - * @param end - {@code int} end index (exclusive). - * @return {@link WeightedList}. - */ - protected WeightedList subList(int start, int end) { - WeightedList list = new WeightedList(); - for (int i = start; i < end; i++) { - list.weights.add(weights.get(i)); - list.values.add(values.get(i)); - } - return list; - } - - /** - * Check if list contains certain value. - * - * @param value - {@link T} value. - * @return {@code true} if value is in list and {@code false} if not. - */ - public boolean contains(T value) { - return values.contains(value); - } - - /** - * Applies {@link Consumer} to all values in list. - * - * @param function - {@link Consumer}. - */ - public void forEach(Consumer function) { - values.forEach(function); - } - - /** - * Get the maximum weight of the tree. - * - * @return {@code float} maximum weight. - */ - public float getMaxWeight() { - return maxWeight; - } -} diff --git a/src/main/java/ru/bclib/world/biomes/BCLBiome.java b/src/main/java/ru/bclib/world/biomes/BCLBiome.java deleted file mode 100644 index 739ffa15..00000000 --- a/src/main/java/ru/bclib/world/biomes/BCLBiome.java +++ /dev/null @@ -1,342 +0,0 @@ -package ru.bclib.world.biomes; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.Climate; -import net.minecraft.world.level.levelgen.SurfaceRules; -import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; -import org.jetbrains.annotations.Nullable; -import ru.bclib.BCLib; -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.util.WeightedList; - -import java.util.List; -import java.util.Map; -import java.util.Random;import net.minecraft.util.RandomSource; -import net.minecraft.world.level.levelgen.WorldgenRandom; - -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -public class BCLBiome extends BCLBiomeSettings { - private final Set> structureTags = Sets.newHashSet(); - private final WeightedList subbiomes = new WeightedList<>(); - private final Map customData = Maps.newHashMap(); - private final ResourceLocation biomeID; - private final Biome biome; - - private final List parameterPoints = Lists.newArrayList(); - - private Consumer> surfaceInit; - private BCLBiome biomeParent; - - /** - * Create wrapper for existing biome using its {@link ResourceLocation} identifier. - * @param biomeKey {@link ResourceKey} for the {@link Biome}. - */ - public BCLBiome(ResourceKey biomeKey) { - this(biomeKey.location()); - } - - /** - * Create wrapper for existing biome using its {@link ResourceLocation} identifier. - * @param biomeID {@link ResourceLocation} biome ID. - */ - public BCLBiome(ResourceLocation biomeID) { - this(biomeID, BuiltinRegistries.BIOME.get(biomeID), null); - } - - /** - * Create wrapper for existing biome using biome instance from {@link BuiltinRegistries}. - * @param biome {@link Biome} to wrap. - */ - public BCLBiome(Biome biome) { - this(biome, null); - } - - /** - * Create wrapper for existing biome using biome instance from {@link BuiltinRegistries}. - * @param biome {@link Biome} to wrap. - * @param settings The Settings for this Biome or {@code null} if you want to apply default settings - */ - public BCLBiome(Biome biome, VanillaBiomeSettings settings) { - this(BiomeAPI.getBiomeID(biome), biome, settings); - } - - public BCLBiome(ResourceLocation biomeID, Biome biome) { - this(biomeID, biome, null); - } - - /** - * Create a new Biome - * @param biomeID {@link ResourceLocation} biome ID. - * @param biome {@link Biome} to wrap. - * @param defaults The Settings for this Biome or null if you want to apply the defaults - */ - public BCLBiome(ResourceLocation biomeID, Biome biome, BCLBiomeSettings defaults) { - this.subbiomes.add(this, 1.0F); - this.biomeID = biomeID; - this.biome = biome; - - if (defaults !=null){ - defaults.applyWithDefaults(this); - } - } - - /** - * Get current biome edge. - * @return {@link BCLBiome} edge. - */ - @Nullable - public BCLBiome getEdge() { - return edge; - } - - /** - * Set biome edge for this biome instance. - * @param edge {@link BCLBiome} as the edge biome. - * @return same {@link BCLBiome}. - */ - BCLBiome setEdge(BCLBiome edge) { - this.edge = edge; - edge.biomeParent = this; - return this; - } - - /** - * Adds sub-biome into this biome instance. Biome chance will be interpreted as a sub-biome generation chance. - * Biome itself has chance 1.0 compared to all its sub-biomes. - * @param biome {@link Random} to be added. - * @return same {@link BCLBiome}. - */ - public BCLBiome addSubBiome(BCLBiome biome) { - biome.biomeParent = this; - subbiomes.add(biome, biome.getGenChance()); - return this; - } - - /** - * Checks if specified biome is a sub-biome of this one. - * @param biome {@link Random}. - * @return true if this instance contains specified biome as a sub-biome. - */ - public boolean containsSubBiome(BCLBiome biome) { - return subbiomes.contains(biome); - } - - /** - * Getter for a random sub-biome from all existing sub-biomes. Will return biome itself if there are no sub-biomes. - * @param random {@link Random}. - * @return {@link BCLBiome}. - */ - public BCLBiome getSubBiome(WorldgenRandom random) { - return subbiomes.get(random); - } - - public void forEachSubBiome(BiConsumer consumer){ - for (int i=0; i getBiomeHolder() { - return BuiltinRegistries.BIOME.getOrCreateHolder(BiomeAPI.getBiomeKey(biome)); - } - /** - * Getter for biome from buil-in registry. For datapack biomes will be same as actual biome. - * @return {@link Biome}. - */ - public Biome getBiome() { - return biome; - } - -// /** -// * Recursively update biomes to correct world biome registry instances, for internal usage only. -// * @param biomeRegistry {@link Registry} for {@link Biome}. -// */ -// public void updateActualBiomes(Registry biomeRegistry) { -// subbiomes.forEach((sub) -> { -// if (sub != this) { -// sub.updateActualBiomes(biomeRegistry); -// } -// }); -// if (edge != null && edge != this) { -// edge.updateActualBiomes(biomeRegistry); -// } -// -// final ResourceKey key = biomeRegistry.getResourceKey(biomeRegistry.get(biomeID)).orElseThrow(); -// Holder aBiome = biomeRegistry.getOrCreateHolder(key); -// if (aBiome != actualBiome && actualBiome != null) { -// System.out.println("Changed actual Biome"); -// } -// this.actualBiome = aBiome; -// if (actualBiome == null) { -// BCLib.LOGGER.error("Unable to find actual Biome for " + biomeID); -// } -// } - - /** - * For internal use from BiomeAPI only - */ - public void afterRegistration(){ - if (!this.structureTags.isEmpty()) { - structureTags.forEach(tagKey -> - TagAPI.addBiomeTag(tagKey, biome) - ); - } - - if (this.surfaceInit != null) { - surfaceInit.accept(getBiomeHolder()); - } - } - - - - /** - * Getter for custom data. Will get custom data object or null if object doesn't exists. - * @param name {@link String} name of data object. - * @return object value or null. - */ - @Nullable - @SuppressWarnings("unchecked") - @Deprecated(forRemoval = true) - public T getCustomData(String name) { - return (T) customData.get(name); - } - - /** - * Getter for custom data. Will get custom data object or default value if object doesn't exists. - * @param name {@link String} name of data object. - * @param defaultValue object default value. - * @return object value or default value. - */ - @SuppressWarnings("unchecked") - @Deprecated(forRemoval = true) - public T getCustomData(String name, T defaultValue) { - return (T) customData.getOrDefault(name, defaultValue); - } - - /** - * Adds custom data object to this biome instance. - * @param name {@link String} name of data object. - * @param obj any data to add. - * @return same {@link BCLBiome}. - */ - public BCLBiome addCustomData(String name, Object obj) { - customData.put(name, obj); - return this; - } - - /** - * Adds custom data object to this biome instance. - * @param data a {@link Map} with custom data. - * @return same {@link BCLBiome}. - */ - public BCLBiome addCustomData(Map data) { - customData.putAll(data); - return this; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - BCLBiome biome = (BCLBiome) obj; - return biome == null ? false : biomeID.equals(biome.biomeID); - } - - @Override - public int hashCode() { - return biomeID.hashCode(); - } - - @Override - public String toString() { - return biomeID.toString(); - } - - /** - * Adds structures to this biome. For internal use only. - * Used inside {@link ru.bclib.api.biomes.BCLBiomeBuilder}. - */ - public void attachStructures(List> structures) { - this.structureTags.addAll(structures); - } - - /** - * Adds structures to this biome. For internal use only. - * Used inside {@link ru.bclib.api.biomes.BCLBiomeBuilder}. - */ - public void addClimateParameters(List params) { - this.parameterPoints.addAll(params); - } - - public void forEachClimateParameter(Consumer consumer){ - this.parameterPoints.forEach(consumer); - } - - /** - * Sets biome surface rule. - * @param surface {@link SurfaceRules.RuleSource} rule. - */ - public void setSurface(RuleSource surface) { - this.surfaceInit = (b) -> { - final ResourceKey key = BiomeAPI.getBiomeKey(b); - if (key == null) { - BCLib.LOGGER.warning("BCL Biome " + biomeID + " does not have registry key!"); - } - else { - BiomeAPI.addSurfaceRule(biomeID, SurfaceRules.ifTrue(SurfaceRules.isBiome(key), surface)); - } - }; - } - - /** - * Returns the group used in the config Files for this biome - * - * Example: {@code Configs.BIOMES_CONFIG.getFloat(configGroup(), "generation_chance", 1.0);} - * @return The group name - */ - public String configGroup() { - return biomeID.getNamespace() + "." + biomeID.getPath(); - } - - private boolean didLoadConfig = false; -} diff --git a/src/main/java/ru/bclib/world/biomes/BCLBiomeSettings.java b/src/main/java/ru/bclib/world/biomes/BCLBiomeSettings.java deleted file mode 100644 index 0b0ae7a8..00000000 --- a/src/main/java/ru/bclib/world/biomes/BCLBiomeSettings.java +++ /dev/null @@ -1,184 +0,0 @@ -package ru.bclib.world.biomes; - -import net.minecraft.world.level.biome.Biome; -import ru.bclib.config.Configs; - -public class BCLBiomeSettings { - public static Builder createBCL(){ - return new Builder(); - } - - public static class Builder extends CommonBuilder{ - public Builder(){ - super(new BCLBiomeSettings()); - } - } - public static class CommonBuilder{ - private final T storage; - CommonBuilder(T storage){ - this.storage = storage; - } - - public T build(){ - return storage; - } - - /** - * Set gen chance for this biome, default value is 1.0. - * @param genChance chance of this biome to be generated. - * @return same {@link BCLBiomeSettings}. - */ - public R setGenChance(float genChance) { - storage.genChance = genChance; - return (R)this; - } - - /** - * Setter for terrain height, can be used in custom terrain generator. - * @param terrainHeight a relative float terrain height value. - * @return same {@link Builder}. - */ - public R setTerrainHeight(float terrainHeight) { - storage.terrainHeight = terrainHeight; - return (R)this; - } - - /** - * Set biome vertical distribution (for tall Nether only). - * @return same {@link Builder}. - */ - public R setVertical() { - return setVertical(true); - } - - /** - * Set biome vertical distribution (for tall Nether only). - * @param vertical {@code boolean} value. - * @return same {@link Builder}. - */ - public R setVertical(boolean vertical) { - storage.vertical = vertical; - return (R)this; - } - - /** - * Set edges size for this biome. Size is in blocks. - * @param size as a float value. - * @return same {@link Builder}. - */ - public R setEdgeSize(int size) { - storage.edgeSize = size; - return (R)this; - } - - /** - * Set edges:biome for this biome. - * @param edge The {@link Biome}. - * @return same {@link Builder}. - */ - public R setEdge(BCLBiome edge) { - storage.edge = edge; - return (R)this; - } - - /** - * Sets fog density for this biome. - * @param fogDensity - * @return same {@link Builder}. - */ - public R setFogDensity(float fogDensity) { - storage.fogDensity = fogDensity; - return (R)this; - } - } - - protected BCLBiomeSettings(){ - this.terrainHeight = 0.1F; - this.fogDensity = 1.0F; - this.genChance = 1.0F; - this.edgeSize = 0; - this.vertical = false; - this.edge = null; - } - - float terrainHeight; - float fogDensity; - float genChance; - int edgeSize; - boolean vertical; - BCLBiome edge; - - - /** - * Getter for biome generation chance, used in {@link ru.bclib.world.generator.BiomePicker} and in custom generators. - * @return biome generation chance as float. - */ - public float getGenChance() { - return this.genChance; - } - - /** - * Checks if biome is vertical, for tall Nether only (or for custom generators). - * @return is biome vertical or not. - */ - public boolean isVertical() { - return vertical; - } - - /** - * Getter for terrain height, can be used in custom terrain generator. - * @return terrain height. - */ - public float getTerrainHeight() { - return terrainHeight; - } - - /** - * Getter for fog density, used in custom for renderer. - * @return fog density as a float. - */ - public float getFogDensity() { - return fogDensity; - } - - /** - * Getter for biome edge size. - * @return edge size in blocks. - */ - public int getEdgeSize() { - return edgeSize; - } - - /** - * Getter for edge-biome. - * @return The assigned edge biome. - */ - public BCLBiome getEdge() { - return edge; - } - - /** - * Load values from Config and apply to the passed Biome. The Default values for the loaded settings - * are derifed from the values store in this object - * @param biome {@link BCLBiome} to assign values to - */ - public void applyWithDefaults(BCLBiome biome){ - final String group = biome.configGroup(); - biome.genChance = Configs.BIOMES_CONFIG.getFloat(group, "generation_chance", this.genChance); - - if (edge!=null){ - biome.edgeSize = Configs.BIOMES_CONFIG.getInt(group, "edge_size", this.edgeSize); - if (edgeSize>0) { - biome.setEdge(edge); - } - } - - if (!(this instanceof VanillaBiomeSettings)){ - biome.fogDensity = Configs.BIOMES_CONFIG.getFloat(group, "fog_density", this.fogDensity); - biome.vertical = Configs.BIOMES_CONFIG.getBoolean(group, "vertical", this.vertical); - biome.terrainHeight = Configs.BIOMES_CONFIG.getFloat(group, "terrain_height", this.terrainHeight); - } - - Configs.BIOMES_CONFIG.saveChanges(); - } -} diff --git a/src/main/java/ru/bclib/world/biomes/FabricBiomesData.java b/src/main/java/ru/bclib/world/biomes/FabricBiomesData.java deleted file mode 100644 index 01d71897..00000000 --- a/src/main/java/ru/bclib/world/biomes/FabricBiomesData.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.bclib.world.biomes; - -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.biome.Biome; - -import java.util.Map; -import java.util.Set; - -public class FabricBiomesData { - public static final Map, Float> END_LAND_BIOMES = Maps.newHashMap(); - public static final Map, Float> END_VOID_BIOMES = Maps.newHashMap(); - public static final Set> NETHER_BIOMES = Sets.newHashSet(); -} diff --git a/src/main/java/ru/bclib/world/biomes/VanillaBiomeSettings.java b/src/main/java/ru/bclib/world/biomes/VanillaBiomeSettings.java deleted file mode 100644 index 1599bd11..00000000 --- a/src/main/java/ru/bclib/world/biomes/VanillaBiomeSettings.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.bclib.world.biomes; - - -public class VanillaBiomeSettings extends BCLBiomeSettings{ - public static class Builder extends BCLBiomeSettings.CommonBuilder{ - public Builder(){ - super(new VanillaBiomeSettings()); - } - } - - public static Builder createVanilla(){ - return new Builder(); - } -} diff --git a/src/main/java/ru/bclib/world/features/BCLFeature.java b/src/main/java/ru/bclib/world/features/BCLFeature.java deleted file mode 100644 index 771016bb..00000000 --- a/src/main/java/ru/bclib/world/features/BCLFeature.java +++ /dev/null @@ -1,90 +0,0 @@ -package ru.bclib.world.features; - -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.data.worldgen.features.FeatureUtils; -import net.minecraft.data.worldgen.placement.PlacementUtils; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.levelgen.GenerationStep.Decoration; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import net.minecraft.world.level.levelgen.placement.PlacementModifier; - -import java.util.Map.Entry; -import java.util.Optional; - -public class BCLFeature { - private final Holder placedFeature; - private final Decoration featureStep; - private final Feature feature; - - - public> BCLFeature(ResourceLocation id, F feature, Decoration featureStep, FC configuration, PlacementModifier[] modifiers) { - this(id, feature, featureStep, buildPlacedFeature(id, feature, configuration, modifiers)); - } - - public BCLFeature(ResourceLocation id, Feature feature, Decoration featureStep, Holder placedFeature) { - this.placedFeature = placedFeature; - this.featureStep = featureStep; - this.feature = feature; - - if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) { - Registry.register(BuiltinRegistries.PLACED_FEATURE, id, placedFeature.value()); - } - if (!Registry.FEATURE.containsKey(id) && !containsObj(Registry.FEATURE, feature)) { - Registry.register(Registry.FEATURE, id, feature); - } - } - - private static > Holder buildPlacedFeature(ResourceLocation id, F feature, FC configuration, PlacementModifier[] modifiers) { - Holder> configuredFeature; - if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) { - configuredFeature = (Holder>)(Object)FeatureUtils.register(id.toString(), feature, configuration); - } else { - configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(), id)).orElseThrow(); - } - - if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) { - return PlacementUtils.register(id.toString(), configuredFeature, modifiers); - } else { - return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(), id)).orElseThrow(); - } - } - - private static boolean containsObj(Registry registry, E obj) { - Optional, E>> optional = registry - .entrySet() - .stream() - .filter(entry -> entry.getValue() == obj) - .findAny(); - return optional.isPresent(); - } - - /** - * Get raw feature. - * @return {@link Feature}. - */ - public Feature getFeature() { - return feature; - } - - /** - * Get configured feature. - * @return {@link PlacedFeature}. - */ - public Holder getPlacedFeature() { - return placedFeature; - } - - /** - * Get feature decoration step. - * @return {@link Decoration}. - */ - public Decoration getDecoration() { - return featureStep; - } -} diff --git a/src/main/java/ru/bclib/world/features/DefaultFeature.java b/src/main/java/ru/bclib/world/features/DefaultFeature.java deleted file mode 100644 index 30553274..00000000 --- a/src/main/java/ru/bclib/world/features/DefaultFeature.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.bclib.world.features; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.Heightmap.Types; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; -import ru.bclib.util.BlocksHelper; - -public abstract class DefaultFeature extends Feature { - protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); - protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); - - public DefaultFeature() { - super(NoneFeatureConfiguration.CODEC); - } - - public static int getYOnSurface(WorldGenLevel world, int x, int z) { - return world.getHeight(Types.WORLD_SURFACE, x, z); - } - - public static int getYOnSurfaceWG(WorldGenLevel world, int x, int z) { - return world.getHeight(Types.WORLD_SURFACE_WG, x, z); - } - - public static BlockPos getPosOnSurface(WorldGenLevel world, BlockPos pos) { - return world.getHeightmapPos(Types.WORLD_SURFACE, pos); - } - - public static BlockPos getPosOnSurfaceWG(WorldGenLevel world, BlockPos pos) { - return world.getHeightmapPos(Types.WORLD_SURFACE_WG, pos); - } - - public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos) { - return getPosOnSurfaceRaycast(world, pos, 256); - } - - public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos, int dist) { - int h = BlocksHelper.downRay(world, pos, dist); - return pos.below(h); - } -} diff --git a/src/main/java/ru/bclib/world/features/ListFeature.java b/src/main/java/ru/bclib/world/features/ListFeature.java deleted file mode 100644 index 02ee5c80..00000000 --- a/src/main/java/ru/bclib/world/features/ListFeature.java +++ /dev/null @@ -1,80 +0,0 @@ -package ru.bclib.world.features; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import ru.bclib.util.StructureHelper; - -import java.util.List; -import java.util.Random;import net.minecraft.util.RandomSource; - -public class ListFeature extends NBTFeature { - private final List list; - private StructureInfo selected; - - public ListFeature(List list, BlockState defaultBlock) { - super(defaultBlock); - this.list = list; - } - - @Override - protected StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, RandomSource random) { - selected = list.get(random.nextInt(list.size())); - return selected.getStructure(); - } - - @Override - protected boolean canSpawn(WorldGenLevel world, BlockPos pos, RandomSource random) { - int cx = pos.getX() >> 4; - int cz = pos.getZ() >> 4; - return ((cx + cz) & 1) == 0 && pos.getY() > 58;// && world.getBlockState(pos.below()).is(EndTags.GEN_TERRAIN); - } - - @Override - protected Rotation getRotation(WorldGenLevel world, BlockPos pos, RandomSource random) { - return Rotation.getRandom(random); - } - - @Override - protected Mirror getMirror(WorldGenLevel world, BlockPos pos, RandomSource random) { - return Mirror.values()[random.nextInt(3)]; - } - - @Override - protected int getYOffset(StructureTemplate structure, WorldGenLevel world, BlockPos pos, RandomSource random) { - return selected.offsetY; - } - - @Override - protected TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, RandomSource random) { - return selected.terrainMerge; - } - - @Override - protected void addStructureData(StructurePlaceSettings data) {} - - public static final class StructureInfo { - public final TerrainMerge terrainMerge; - public final String structurePath; - public final int offsetY; - - private StructureTemplate structure; - - public StructureInfo(String structurePath, int offsetY, TerrainMerge terrainMerge) { - this.terrainMerge = terrainMerge; - this.structurePath = structurePath; - this.offsetY = offsetY; - } - - public StructureTemplate getStructure() { - if (structure == null) { - structure = StructureHelper.readStructure(structurePath); - } - return structure; - } - } -} diff --git a/src/main/java/ru/bclib/world/features/NBTFeature.java b/src/main/java/ru/bclib/world/features/NBTFeature.java deleted file mode 100644 index 91c43bb1..00000000 --- a/src/main/java/ru/bclib/world/features/NBTFeature.java +++ /dev/null @@ -1,235 +0,0 @@ -package ru.bclib.world.features; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.BlockPos.MutableBlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.Vec3i; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; -import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; - -import ru.bclib.api.biomes.BiomeAPI; -import ru.bclib.api.tag.CommonBlockTags; -import ru.bclib.util.BlocksHelper; -import ru.bclib.world.processors.DestructionStructureProcessor; - -import java.io.IOException; -import java.io.InputStream; - -public abstract class NBTFeature extends DefaultFeature { - private final BlockState defaultBlock; - public NBTFeature(BlockState defaultBlock){ - this.defaultBlock = defaultBlock; - } - - protected static final DestructionStructureProcessor DESTRUCTION = new DestructionStructureProcessor(); - - protected abstract StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, RandomSource random); - - protected abstract boolean canSpawn(WorldGenLevel world, BlockPos pos, RandomSource random); - - protected abstract Rotation getRotation(WorldGenLevel world, BlockPos pos, RandomSource random); - - protected abstract Mirror getMirror(WorldGenLevel world, BlockPos pos, RandomSource random); - - protected abstract int getYOffset(StructureTemplate structure, WorldGenLevel world, BlockPos pos, RandomSource random); - - protected abstract TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, RandomSource random); - - protected abstract void addStructureData(StructurePlaceSettings data); - - protected BlockPos getGround(WorldGenLevel world, BlockPos center) { - Holder biome = world.getBiome(center); - ResourceLocation id = BiomeAPI.getBiomeID(biome); - if (id.getNamespace().contains("moutain") || id.getNamespace().contains("lake")) { - int y = getAverageY(world, center); - return new BlockPos(center.getX(), y, center.getZ()); - } - else { - int y = getAverageYWG(world, center); - return new BlockPos(center.getX(), y, center.getZ()); - } - } - - protected int getAverageY(WorldGenLevel world, BlockPos center) { - int y = getYOnSurface(world, center.getX(), center.getZ()); - y += getYOnSurface(world, center.getX() - 2, center.getZ() - 2); - y += getYOnSurface(world, center.getX() + 2, center.getZ() - 2); - y += getYOnSurface(world, center.getX() - 2, center.getZ() + 2); - y += getYOnSurface(world, center.getX() + 2, center.getZ() + 2); - return y / 5; - } - - protected int getAverageYWG(WorldGenLevel world, BlockPos center) { - int y = getYOnSurfaceWG(world, center.getX(), center.getZ()); - y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() - 2); - y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() - 2); - y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() + 2); - y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() + 2); - return y / 5; - } - - @Override - public boolean place(FeaturePlaceContext context) { - WorldGenLevel world = context.level(); - RandomSource random = context.random(); - BlockPos center = context.origin(); - - center = new BlockPos(((center.getX() >> 4) << 4) | 8, 128, ((center.getZ() >> 4) << 4) | 8); - center = getGround(world, center); - - if (!canSpawn(world, center, random)) { - return false; - } - - int posY = center.getY() + 1; - StructureTemplate structure = getStructure(world, center, random); - Rotation rotation = getRotation(world, center, random); - Mirror mirror = getMirror(world, center, random); - BlockPos offset = StructureTemplate.transform( - new BlockPos(structure.getSize()), - mirror, - rotation, - BlockPos.ZERO - ); - center = center.offset(0, getYOffset(structure, world, center, random) + 0.5, 0); - - BoundingBox bounds = makeBox(center); - StructurePlaceSettings placementData = new StructurePlaceSettings() - .setRotation(rotation) - .setMirror(mirror) - .setBoundingBox(bounds); - addStructureData(placementData); - center = center.offset(-offset.getX() * 0.5, 0, -offset.getZ() * 0.5); - structure.placeInWorld(world, center, center, placementData, random, 4); - - TerrainMerge merge = getTerrainMerge(world, center, random); - int x1 = center.getX(); - int z1 = center.getZ(); - int x2 = x1 + offset.getX(); - int z2 = z1 + offset.getZ(); - if (merge != TerrainMerge.NONE) { - MutableBlockPos mut = new MutableBlockPos(); - - if (x2 < x1) { - int a = x1; - x1 = x2; - x2 = a; - } - - if (z2 < z1) { - int a = z1; - z1 = z2; - z2 = a; - } - - int surfMax = posY - 1; - for (int x = x1; x <= x2; x++) { - mut.setX(x); - for (int z = z1; z <= z2; z++) { - mut.setZ(z); - mut.setY(surfMax); - BlockState state = world.getBlockState(mut); - if (!isTerrain(state) && state.isFaceSturdy(world, mut, Direction.DOWN)) { - for (int i = 0; i < 10; i++) { - mut.setY(mut.getY() - 1); - BlockState stateSt = world.getBlockState(mut); - if (!isTerrain(stateSt)) { - if (merge == TerrainMerge.SURFACE) { - boolean isTop = mut.getY() == surfMax && state.getMaterial().isSolidBlocking(); - Holder b = world.getBiome(mut); - BlockState top = (isTop ? BiomeAPI.findTopMaterial(b) : BiomeAPI.findUnderMaterial(b)).orElse(defaultBlock); - BlocksHelper.setWithoutUpdate(world, mut, top); - } - else { - BlocksHelper.setWithoutUpdate(world, mut, state); - } - } - else { - if (isTerrain(state) && state.getMaterial().isSolidBlocking()) { - if (merge == TerrainMerge.SURFACE) { - Holder b = world.getBiome(mut); - BlockState bottom = BiomeAPI.findUnderMaterial(b).orElse(defaultBlock); - BlocksHelper.setWithoutUpdate(world, mut, bottom); - } - else { - BlocksHelper.setWithoutUpdate(world, mut, state); - } - } - break; - } - } - } - } - } - } - //BlocksHelper.fixBlocks(world, new BlockPos(x1, center.getY(), z1), new BlockPos(x2, center.getY() + offset.getY(), z2)); - - return true; - } - - private boolean isTerrain(BlockState state) { - return state.is(CommonBlockTags.END_STONES) || state.is(CommonBlockTags.NETHER_STONES); - } - - protected BoundingBox makeBox(BlockPos pos) { - int sx = ((pos.getX() >> 4) << 4) - 16; - int sz = ((pos.getZ() >> 4) << 4) - 16; - int ex = sx + 47; - int ez = sz + 47; - return BoundingBox.fromCorners(new Vec3i(sx, 0, sz), new Vec3i(ex, 255, ez)); - } - - protected static StructureTemplate readStructure(ResourceLocation resource) { - String ns = resource.getNamespace(); - String nm = resource.getPath(); - - try { - InputStream inputstream = MinecraftServer.class.getResourceAsStream("/data/" + ns + "/structures/" + nm + ".nbt"); - return readStructureFromStream(inputstream); - } - catch (IOException e) { - e.printStackTrace(); - } - - return null; - } - - private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException { - CompoundTag nbttagcompound = NbtIo.readCompressed(stream); - - StructureTemplate template = new StructureTemplate(); - template.load(nbttagcompound); - - return template; - } - - public enum TerrainMerge { - NONE, SURFACE, OBJECT; - - public static TerrainMerge getFromString(String type) { - if (type.equals("surface")) { - return SURFACE; - } - else if (type.equals("object")) { - return OBJECT; - } - else { - return NONE; - } - } - } -} diff --git a/src/main/java/ru/bclib/world/generator/BCLBiomeSource.java b/src/main/java/ru/bclib/world/generator/BCLBiomeSource.java deleted file mode 100644 index 44d4e469..00000000 --- a/src/main/java/ru/bclib/world/generator/BCLBiomeSource.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.bclib.world.generator; - -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import ru.bclib.api.biomes.BiomeAPI; - -import java.util.List; - -public abstract class BCLBiomeSource extends BiomeSource { - protected final Registry biomeRegistry; - protected long currentSeed; - - private static List> preInit(Registry biomeRegistry, List> biomes){ - biomes.forEach(biome -> BiomeAPI.sortBiomeFeatures(biome)); - return biomes; - } - - protected BCLBiomeSource(Registry biomeRegistry, List> list) { - super(preInit(biomeRegistry, list)); - System.out.println(this + " with Registry: "+ biomeRegistry.getClass().getName() + "@" + Integer.toHexString(biomeRegistry.hashCode())); - this.biomeRegistry = biomeRegistry; - - BiomeAPI.initRegistry(biomeRegistry); - } - - public void setSeed(long seed){ - System.out.println(this+" set Seed: " + seed); - this.currentSeed = seed; - } -} diff --git a/src/main/java/ru/bclib/world/generator/BiomePicker.java b/src/main/java/ru/bclib/world/generator/BiomePicker.java deleted file mode 100644 index 9a4c62f6..00000000 --- a/src/main/java/ru/bclib/world/generator/BiomePicker.java +++ /dev/null @@ -1,106 +0,0 @@ -package ru.bclib.world.generator; - -import com.google.common.collect.Lists; - -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; - -import ru.bclib.util.WeighTree; -import ru.bclib.util.WeightedList; -import ru.bclib.world.biomes.BCLBiome; - -import java.util.*; - -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.levelgen.WorldgenRandom; - -public class BiomePicker { - public final Map all = new HashMap<>(); - public class ActualBiome { - public final BCLBiome bclBiome; - public final Holder biome; - public final ResourceKey key; - - private final WeightedList subbiomes = new WeightedList<>(); - private final ActualBiome edge; - private final ActualBiome parent; - - private ActualBiome(BCLBiome bclBiome){ - all.put(bclBiome, this); - this.bclBiome = bclBiome; - - this.key = biomeRegistry.getResourceKey(biomeRegistry.get(bclBiome.getID())).orElseThrow(); - this.biome = biomeRegistry.getOrCreateHolder(key); - - bclBiome.forEachSubBiome((b, w)->{ - subbiomes.add(create(b), w); - }); - - edge = bclBiome.getEdge()!=null?create(bclBiome.getEdge()):null; - parent = bclBiome.getParentBiome()!=null?create(bclBiome.getParentBiome()):null; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ActualBiome entry = (ActualBiome) o; - return bclBiome.equals(entry.bclBiome); - } - - @Override - public int hashCode() { - return Objects.hash(bclBiome); - } - - public ActualBiome getSubBiome(WorldgenRandom random) { - return subbiomes.get(random); - } - - public ActualBiome getEdge(){ - return edge; - } - - public ActualBiome getParentBiome(){ - return parent; - } - - public boolean isSame(ActualBiome e){ - return bclBiome.isSame(e.bclBiome); - } - } - - private ActualBiome create(BCLBiome bclBiome){ - ActualBiome e = all.get(bclBiome); - if (e!=null) return e; - return new ActualBiome(bclBiome); - } - - private final List biomes = Lists.newArrayList(); - public final Registry biomeRegistry; - private WeighTree tree; - - public BiomePicker(Registry biomeRegistry){ - this.biomeRegistry = biomeRegistry; - } - - public void addBiome(BCLBiome biome) { - biomes.add(create(biome)); - } - - public ActualBiome getBiome(WorldgenRandom random) { - return biomes.isEmpty() ? null : tree.get(random); - } - - public void rebuild() { - if (biomes.isEmpty()) { - return; - } - WeightedList list = new WeightedList<>(); - biomes.forEach(biome -> { - list.add(biome, biome.bclBiome.getGenChance()); - }); - tree = new WeighTree<>(list); - } -} diff --git a/src/main/java/ru/bclib/world/generator/BiomeType.java b/src/main/java/ru/bclib/world/generator/BiomeType.java deleted file mode 100644 index 7556894d..00000000 --- a/src/main/java/ru/bclib/world/generator/BiomeType.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.bclib.world.generator; - -public enum BiomeType { - LAND, VOID; -} diff --git a/src/main/java/ru/bclib/world/generator/GeneratorOptions.java b/src/main/java/ru/bclib/world/generator/GeneratorOptions.java deleted file mode 100644 index b5e98181..00000000 --- a/src/main/java/ru/bclib/world/generator/GeneratorOptions.java +++ /dev/null @@ -1,103 +0,0 @@ -package ru.bclib.world.generator; - -import net.minecraft.util.Mth; -import ru.bclib.config.Configs; - -import java.awt.Point; -import java.util.function.Function; - -public class GeneratorOptions { - private static int biomeSizeNether; - private static int biomeVSizeNether; - private static int biomeSizeEndLand; - private static int biomeSizeEndVoid; - private static Function endLandFunction; - private static boolean customNetherBiomeSource = true; - private static boolean customEndBiomeSource = true; - private static boolean useOldBiomeGenerator = false; - private static boolean verticalBiomes = true; - private static long farEndBiomesSqr = 1000000; - private static boolean fixEndBiomeSource = true; - private static boolean fixNetherBiomeSource = true; - - public static void init() { - biomeSizeNether = Configs.GENERATOR_CONFIG.getInt("nether.biomeMap", "biomeSize", 256); - biomeVSizeNether = Configs.GENERATOR_CONFIG.getInt("nether.biomeMap", "biomeVerticalSize(onlyInTallNether)", 86); - 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); - useOldBiomeGenerator = Configs.GENERATOR_CONFIG.useOldGenerator(); - verticalBiomes = Configs.GENERATOR_CONFIG.getBoolean("options", "verticalBiomesInTallNether", true); - fixEndBiomeSource = Configs.GENERATOR_CONFIG.getBoolean("options.biomeSource", "fixEndBiomeSource", true); - fixNetherBiomeSource = Configs.GENERATOR_CONFIG.getBoolean("options.biomeSource", "fixNetherBiomeSource", true); - } - - public static int getBiomeSizeNether() { - return Mth.clamp(biomeSizeNether, 1, 8192); - } - - public static int getVerticalBiomeSizeNether() { - return Mth.clamp(biomeVSizeNether, 1, 8192); - } - - public static int getBiomeSizeEndLand() { - return Mth.clamp(biomeSizeEndLand, 1, 8192); - } - - public static int getBiomeSizeEndVoid() { - return Mth.clamp(biomeSizeEndVoid, 1, 8192); - } - - public static void setEndLandFunction(Function endLandFunction) { - GeneratorOptions.endLandFunction = endLandFunction; - } - - public static Function getEndLandFunction() { - return endLandFunction; - } - - public static long getFarEndBiomes() { - return farEndBiomesSqr; - } - - /** - * Set distance of far End biomes generation, in blocks - * @param distance - */ - public static void setFarEndBiomes(int distance) { - GeneratorOptions.farEndBiomesSqr = (long) distance * (long) distance; - } - - /** - * Set distance of far End biomes generation, in blocks^2 - * @param distanceSqr the distance squared - */ - public static void setFarEndBiomesSqr(long distanceSqr) { - GeneratorOptions.farEndBiomesSqr = distanceSqr; - } - - public static boolean customNetherBiomeSource() { - return customNetherBiomeSource; - } - - public static boolean customEndBiomeSource() { - return customEndBiomeSource; - } - - public static boolean useOldBiomeGenerator() { - return useOldBiomeGenerator; - } - - public static boolean useVerticalBiomes() { - return verticalBiomes; - } - - public static boolean fixEndBiomeSource() { - return fixEndBiomeSource; - } - - public static boolean fixNetherBiomeSource() { - return fixNetherBiomeSource; - } -} diff --git a/src/main/java/ru/bclib/world/generator/map/MapStack.java b/src/main/java/ru/bclib/world/generator/map/MapStack.java deleted file mode 100644 index 1cec112b..00000000 --- a/src/main/java/ru/bclib/world/generator/map/MapStack.java +++ /dev/null @@ -1,103 +0,0 @@ -package ru.bclib.world.generator.map; - -import net.minecraft.util.Mth; -import org.apache.commons.lang3.function.TriFunction; -import ru.bclib.interfaces.BiomeChunk; -import ru.bclib.interfaces.BiomeMap; -import ru.bclib.interfaces.TriConsumer; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.world.generator.BiomePicker; - -import java.util.Random; - -public class MapStack implements BiomeMap { - private final OpenSimplexNoise noise; - private final BiomeMap[] maps; - private final double layerDistortion; - private final int worldHeight; - private final int minValue; - private final int maxValue; - private final int maxIndex; - - public MapStack(long seed, int size, BiomePicker picker, int mapHeight, int worldHeight, TriFunction mapConstructor) { - final int mapCount = Mth.ceil((float) worldHeight / mapHeight); - this.maxIndex = mapCount - 1; - this.worldHeight = worldHeight; - this.layerDistortion = mapHeight * 0.1; - minValue = Mth.floor(mapHeight * 0.5F + 0.5F); - maxValue = Mth.floor(worldHeight - mapHeight * 0.5F + 0.5F); - maps = new BiomeMap[mapCount]; - Random random = new Random(seed); - for (int i = 0; i < mapCount; i++) { - maps[i] = mapConstructor.apply(random.nextLong(), size, picker); - maps[i].setChunkProcessor(this::onChunkCreation); - } - noise = new OpenSimplexNoise(random.nextInt()); - } - - @Override - public void clearCache() { - for (BiomeMap map: maps) { - map.clearCache(); - } - } - - @Override - public void setChunkProcessor(TriConsumer processor) {} - - @Override - public BiomeChunk getChunk(int cx, int cz, boolean update) { - return null; - } - - @Override - public BiomePicker.ActualBiome getBiome(double x, double y, double z) { - int mapIndex; - - if (y < minValue) { - mapIndex = 0; - } - else if (y > maxValue) { - mapIndex = maxIndex; - } - else { - mapIndex = Mth.floor((y + noise.eval(x * 0.03, z * 0.03) * layerDistortion) / worldHeight * maxIndex + 0.5F); - mapIndex = Mth.clamp(mapIndex, 0, maxIndex); - } - - return maps[mapIndex].getBiome(x, y, z); - } - - private void onChunkCreation(int cx, int cz, int side) { - BiomePicker.ActualBiome[][] biomeMap = new BiomePicker.ActualBiome[side][side]; - BiomeChunk[] chunks = new BiomeChunk[maps.length]; - - boolean isNoEmpty = false; - for (int i = 0; i < maps.length; i++) { - chunks[i] = maps[i].getChunk(cx, cz, false); - for (int x = 0; x < side; x++) { - for (int z = 0; z < side; z++) { - if (biomeMap[x][z] == null) { - BiomePicker.ActualBiome biome = chunks[i].getBiome(x, z); - if (biome.bclBiome.isVertical()) { - biomeMap[x][z] = biome; - isNoEmpty = true; - } - } - } - } - } - - if (isNoEmpty) { - for (int i = 0; i < maps.length; i++) { - for (int x = 0; x < side; x++) { - for (int z = 0; z < side; z++) { - if (biomeMap[x][z] != null) { - chunks[i].setBiome(x, z, biomeMap[x][z]); - } - } - } - } - } - } -} diff --git a/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java b/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java deleted file mode 100644 index c9cf1bbb..00000000 --- a/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java +++ /dev/null @@ -1,157 +0,0 @@ -package ru.bclib.world.generator.map.hex; - -import ru.bclib.interfaces.BiomeChunk; -import ru.bclib.world.generator.BiomePicker; - -import java.util.Arrays; - -import net.minecraft.world.level.levelgen.WorldgenRandom; - -public class HexBiomeChunk implements BiomeChunk { - private static final short SIDE = 32; - private static final byte SIDE_PRE = 4; - private static final short SIZE = SIDE * SIDE; - private static final short MAX_SIDE = SIZE - SIDE; - private static final byte SCALE_PRE = SIDE / SIDE_PRE; - private static final byte SIZE_PRE = SIDE_PRE * SIDE_PRE; - private static final byte SIDE_MASK = SIDE - 1; - private static final byte SIDE_PRE_MASK = SIDE_PRE - 1; - private static final byte SIDE_OFFSET = (byte) Math.round(Math.log(SIDE) / Math.log(2)); - private static final byte SIDE_PRE_OFFSET = (byte) Math.round(Math.log(SIDE_PRE) / Math.log(2)); - private static final short[][] NEIGHBOURS; - - private final BiomePicker.ActualBiome[] biomes = new BiomePicker.ActualBiome[SIZE]; - - public HexBiomeChunk(WorldgenRandom random, BiomePicker picker) { - BiomePicker.ActualBiome[][] buffers = new BiomePicker.ActualBiome[2][SIZE]; - - for (BiomePicker.ActualBiome[] buffer: buffers) { - Arrays.fill(buffer, null); - } - - for (byte index = 0; index < SIZE_PRE; index++) { - byte px = (byte) (index >> SIDE_PRE_OFFSET); - byte pz = (byte) (index & SIDE_PRE_MASK); - px = (byte) (px * SCALE_PRE + random.nextInt(SCALE_PRE)); - pz = (byte) (pz * SCALE_PRE + random.nextInt(SCALE_PRE)); - circle(buffers[0], getIndex(px, pz), picker.getBiome(random), null); - } - - boolean hasEmptyCells = true; - byte bufferIndex = 0; - while (hasEmptyCells) { - BiomePicker.ActualBiome[] inBuffer = buffers[bufferIndex]; - bufferIndex = (byte) ((bufferIndex + 1) & 1); - BiomePicker.ActualBiome[] outBuffer = buffers[bufferIndex]; - hasEmptyCells = false; - - for (short index = SIDE; index < MAX_SIDE; index++) { - byte z = (byte) (index & SIDE_MASK); - if (z == 0 || z == SIDE_MASK) { - continue; - } - if (inBuffer[index] != null) { - outBuffer[index] = inBuffer[index]; - short[] neighbours = getNeighbours(index & SIDE_MASK); - short indexSide = (short) (index + neighbours[random.nextInt(6)]); - if (indexSide >= 0 && indexSide < SIZE && outBuffer[indexSide] == null) { - outBuffer[indexSide] = inBuffer[index]; - } - } - else { - hasEmptyCells = true; - } - } - } - - BiomePicker.ActualBiome[] outBuffer = buffers[bufferIndex]; - byte preN = (byte) (SIDE_MASK - 2); - for (byte index = 0; index < SIDE; index++) { - outBuffer[getIndex(index, (byte) 0)] = outBuffer[getIndex(index, (byte) 2)]; - outBuffer[getIndex((byte) 0, index)] = outBuffer[getIndex((byte) 2, index)]; - outBuffer[getIndex(index, SIDE_MASK)] = outBuffer[getIndex(index, preN)]; - outBuffer[getIndex(SIDE_MASK, index)] = outBuffer[getIndex(preN, index)]; - } - - for (short index = 0; index < SIZE; index++) { - if (outBuffer[index] == null) { - outBuffer[index] = picker.getBiome(random); - } - else if (random.nextInt(4) == 0) { - circle(outBuffer, index, outBuffer[index].getSubBiome(random), outBuffer[index]); - } - } - - System.arraycopy(outBuffer, 0, this.biomes, 0, SIZE); - } - - private void circle(BiomePicker.ActualBiome[] buffer, short center, BiomePicker.ActualBiome biome, BiomePicker.ActualBiome mask) { - if (buffer[center] == mask) { - buffer[center] = biome; - } - short[] neighbours = getNeighbours(center & SIDE_MASK); - for (short i: neighbours) { - short index = (short) (center + i); - if (index >= 0 && index < SIZE && buffer[index] == mask) { - buffer[index] = biome; - } - } - } - - private static byte wrap(int value) { - return (byte) (value & SIDE_MASK); - } - - private short getIndex(byte x, byte z) { - return (short) ((short) x << SIDE_OFFSET | z); - } - - @Override - public BiomePicker.ActualBiome getBiome(int x, int z) { - return biomes[getIndex(wrap(x), wrap(z))]; - } - - @Override - public void setBiome(int x, int z, BiomePicker.ActualBiome biome) { - biomes[getIndex(wrap(x), wrap(z))] = biome; - } - - @Override - public int getSide() { - return SIDE; - } - - public static int scaleCoordinate(int value) { - return value >> SIDE_OFFSET; - } - - public static boolean isBorder(int value) { - return wrap(value) == SIDE_MASK; - } - - private short[] getNeighbours(int z) { - return NEIGHBOURS[z & 1]; - } - - public static float scaleMap(float size) { - return size / (SIDE >> 2); - } - - static { - NEIGHBOURS = new short[2][6]; - - NEIGHBOURS[0][0] = 1; - NEIGHBOURS[0][1] = -1; - NEIGHBOURS[0][2] = SIDE; - NEIGHBOURS[0][3] = -SIDE; - NEIGHBOURS[0][4] = SIDE + 1; - NEIGHBOURS[0][5] = SIDE - 1; - - NEIGHBOURS[1][0] = 1; - NEIGHBOURS[1][1] = -1; - NEIGHBOURS[1][2] = SIDE; - NEIGHBOURS[1][3] = -SIDE; - NEIGHBOURS[1][4] = -SIDE + 1; - NEIGHBOURS[1][5] = -SIDE - 1; - } -} diff --git a/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java b/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java deleted file mode 100644 index 6e80dc78..00000000 --- a/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java +++ /dev/null @@ -1,184 +0,0 @@ -package ru.bclib.world.generator.map.hex; - -import com.google.common.collect.Maps; -import net.minecraft.world.level.ChunkPos; -import ru.bclib.interfaces.BiomeChunk; -import ru.bclib.interfaces.BiomeMap; -import ru.bclib.interfaces.TriConsumer; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.util.MHelper; -import ru.bclib.world.generator.BiomePicker; - -import java.util.Map; -import java.util.Random;import net.minecraft.util.RandomSource; -import net.minecraft.world.level.levelgen.WorldgenRandom; - -public class HexBiomeMap implements BiomeMap { - private static final float RAD_INNER = (float) Math.sqrt(3.0) * 0.5F; - private static final float COEF = 0.25F * (float) Math.sqrt(3.0); - private static final float COEF_HALF = COEF * 0.5F; - private static final float SIN = (float) Math.sin(0.4); - private static final float COS = (float) Math.cos(0.4); - private static final float[] EDGE_CIRCLE_X; - private static final float[] EDGE_CIRCLE_Z; - - private final Map chunks = Maps.newConcurrentMap(); - private final BiomePicker picker; - - private final OpenSimplexNoise[] noises = new OpenSimplexNoise[2]; - private TriConsumer processor; - private final byte noiseIterations; - private final float scale; - private final int seed; - - public HexBiomeMap(long seed, int size, BiomePicker picker) { - this.picker = picker; - this.scale = HexBiomeChunk.scaleMap(size); - Random random = new Random(seed); - noises[0] = new OpenSimplexNoise(random.nextInt()); - noises[1] = new OpenSimplexNoise(random.nextInt()); - noiseIterations = (byte) Math.min(Math.ceil(Math.log(scale) / Math.log(2)), 5); - this.seed = random.nextInt(); - } - - @Override - public void clearCache() { - if (chunks.size() > 127) { - chunks.clear(); - } - } - - @Override - public BiomePicker.ActualBiome getBiome(double x, double y, double z) { - BiomePicker.ActualBiome biome = getRawBiome(x, z); - BiomePicker.ActualBiome edge = biome.getEdge(); - int size = biome.bclBiome.getEdgeSize(); - - if (edge == null && biome.getParentBiome() != null) { - edge = biome.getParentBiome().getEdge(); - size = biome.getParentBiome().bclBiome.getEdgeSize(); - } - - if (edge == null) { - return biome; - } - - for (byte i = 0; i < 8; i++) { - if (!getRawBiome(x + size * EDGE_CIRCLE_X[i], z + size * EDGE_CIRCLE_Z[i]).isSame(biome)) { - return edge; - } - } - - return biome; - } - - @Override - public BiomeChunk getChunk(final int cx, final int cz, final boolean update) { - final ChunkPos pos = new ChunkPos(cx, cz); - HexBiomeChunk chunk = chunks.get(pos); - if (chunk == null) { - WorldgenRandom random = new WorldgenRandom(RandomSource.create(MHelper.getSeed(seed, cx, cz))); - chunk = new HexBiomeChunk(random, picker); - if (update && processor != null) { - processor.accept(cx, cz, chunk.getSide()); - } - chunks.put(pos, chunk); - } - return chunk; - } - - @Override - public void setChunkProcessor(TriConsumer processor) { - this.processor = processor; - } - - private BiomePicker.ActualBiome getRawBiome(double x, double z) { - double px = x / scale * RAD_INNER; - double pz = z / scale; - double dx = rotateX(px, pz); - double dz = rotateZ(px, pz); - px = dx; - pz = dz; - - dx = getNoise(px, pz, (byte) 0) * 0.2F; - dz = getNoise(pz, px, (byte) 1) * 0.2F; - px += dx; - pz += dz; - - int cellZ = (int) Math.floor(pz); - boolean offset = (cellZ & 1) == 1; - - if (offset) { - px += 0.5; - } - - int cellX = (int) Math.floor(px); - - float pointX = (float) (px - cellX - 0.5); - float pointZ = (float) (pz - cellZ - 0.5); - - if (Math.abs(pointZ) < 0.3333F) { - return getChunkBiome(cellX, cellZ); - } - - if (insideHexagon(0, 0, 1.1555F, pointZ * RAD_INNER, pointX)) { - return getChunkBiome(cellX, cellZ); - } - - cellX = pointX < 0 ? (offset ? cellX - 1 : cellX) : (offset ? cellX : cellX + 1); - cellZ = pointZ < 0 ? cellZ - 1 : cellZ + 1; - - return getChunkBiome(cellX, cellZ); - } - - private BiomePicker.ActualBiome getChunkBiome(int x, int z) { - int cx = HexBiomeChunk.scaleCoordinate(x); - int cz = HexBiomeChunk.scaleCoordinate(z); - - if (((z >> 2) & 1) == 0 && HexBiomeChunk.isBorder(x)) { - x = 0; - cx += 1; - } - else if (((x >> 2) & 1) == 0 && HexBiomeChunk.isBorder(z)) { - z = 0; - cz += 1; - } - - return getChunk(cx, cz, true).getBiome(x, z); - } - - private boolean insideHexagon(float centerX, float centerZ, float radius, float x, float z) { - double dx = Math.abs(x - centerX) / radius; - double dy = Math.abs(z - centerZ) / radius; - return (dy <= COEF) && (COEF * dx + 0.25F * dy <= COEF_HALF); - } - - private double getNoise(double x, double z, byte state) { - double result = 0; - for (byte i = 1; i <= noiseIterations; i++) { - OpenSimplexNoise noise = noises[state]; - state = (byte) ((state + 1) & 1); - result += noise.eval(x * i, z * i) / i; - } - return result; - } - - private double rotateX(double x, double z) { - return x * COS - z * SIN; - } - - private double rotateZ(double x, double z) { - return x * SIN + z * COS; - } - - static { - EDGE_CIRCLE_X = new float[8]; - EDGE_CIRCLE_Z = new float[8]; - - for (byte i = 0; i < 8; i++) { - float angle = i / 4F * (float) Math.PI; - EDGE_CIRCLE_X[i] = (float) Math.sin(angle); - EDGE_CIRCLE_Z[i] = (float) Math.cos(angle); - } - } -} diff --git a/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeChunk.java b/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeChunk.java deleted file mode 100644 index a7757a7b..00000000 --- a/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeChunk.java +++ /dev/null @@ -1,66 +0,0 @@ -package ru.bclib.world.generator.map.square; - -import ru.bclib.interfaces.BiomeChunk; -import ru.bclib.world.generator.BiomePicker; - -import net.minecraft.world.level.levelgen.WorldgenRandom; - -public class SquareBiomeChunk implements BiomeChunk { - private static final int BIT_OFFSET = 4; - protected static final int WIDTH = 1 << BIT_OFFSET; - private static final int SM_WIDTH = WIDTH >> 1; - private static final int SM_BIT_OFFSET = BIT_OFFSET >> 1; - private static final int MASK_OFFSET = SM_WIDTH - 1; - protected static final int MASK_WIDTH = WIDTH - 1; - - private static final int SM_CAPACITY = SM_WIDTH * SM_WIDTH; - private static final int CAPACITY = WIDTH * WIDTH; - - private final BiomePicker.ActualBiome[] biomes; - - public SquareBiomeChunk(WorldgenRandom random, BiomePicker picker) { - BiomePicker.ActualBiome[] PreBio = new BiomePicker.ActualBiome[SM_CAPACITY]; - biomes = new BiomePicker.ActualBiome[CAPACITY]; - - for (int x = 0; x < SM_WIDTH; x++) { - int offset = x << SM_BIT_OFFSET; - for (int z = 0; z < SM_WIDTH; z++) { - PreBio[offset | z] = picker.getBiome(random); - } - } - - for (int x = 0; x < WIDTH; x++) { - int offset = x << BIT_OFFSET; - for (int z = 0; z < WIDTH; z++) { - biomes[offset | z] = PreBio[getSmIndex(offsetXZ(x, random), offsetXZ(z, random))].getSubBiome(random); - } - } - } - - @Override - public BiomePicker.ActualBiome getBiome(int x, int z) { - return biomes[getIndex(x & MASK_WIDTH, z & MASK_WIDTH)]; - } - - @Override - public void setBiome(int x, int z, BiomePicker.ActualBiome biome) { - biomes[getIndex(x & MASK_WIDTH, z & MASK_WIDTH)] = biome; - } - - @Override - public int getSide() { - return WIDTH; - } - - private int offsetXZ(int x, WorldgenRandom random) { - return ((x + random.nextInt(2)) >> 1) & MASK_OFFSET; - } - - private int getIndex(int x, int z) { - return x << BIT_OFFSET | z; - } - - private int getSmIndex(int x, int z) { - return x << SM_BIT_OFFSET | z; - } -} diff --git a/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeMap.java b/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeMap.java deleted file mode 100644 index 8aea90b9..00000000 --- a/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeMap.java +++ /dev/null @@ -1,138 +0,0 @@ -package ru.bclib.world.generator.map.square; - -import com.google.common.collect.Maps; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.levelgen.LegacyRandomSource; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import ru.bclib.interfaces.BiomeChunk; -import ru.bclib.interfaces.BiomeMap; -import ru.bclib.interfaces.TriConsumer; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.util.MHelper; -import ru.bclib.world.generator.BiomePicker; - -import java.util.Map; - -public class SquareBiomeMap implements BiomeMap { - private final Map maps = Maps.newHashMap(); - private final OpenSimplexNoise noiseX; - private final OpenSimplexNoise noiseZ; - private final WorldgenRandom random; - private final BiomePicker picker; - - private final int sizeXZ; - private final int depth; - private final int size; - - private TriConsumer processor; - - public SquareBiomeMap(long seed, int size, BiomePicker picker) { - random = new WorldgenRandom(new LegacyRandomSource(seed)); - noiseX = new OpenSimplexNoise(random.nextLong()); - noiseZ = new OpenSimplexNoise(random.nextLong()); - this.sizeXZ = size; - depth = (int) Math.ceil(Math.log(size) / Math.log(2)) - 2; - this.size = 1 << depth; - this.picker = picker; - } - - @Override - public void clearCache() { - if (maps.size() > 32) { - maps.clear(); - } - } - - @Override - public BiomePicker.ActualBiome getBiome(double x, double y, double z) { - BiomePicker.ActualBiome biome = getRawBiome(x, z); - - if (biome.getEdge() != null || (biome.getParentBiome() != null && biome.getParentBiome().getEdge() != null)) { - BiomePicker.ActualBiome search = biome; - if (biome.getParentBiome() != null) { - search = biome.getParentBiome(); - } - - int size = search.bclBiome.getEdgeSize(); - boolean edge = !search.isSame(getRawBiome(x + size, z)); - edge = edge || !search.isSame(getRawBiome(x - size, z)); - edge = edge || !search.isSame(getRawBiome(x, z + size)); - edge = edge || !search.isSame(getRawBiome(x, z - size)); - edge = edge || !search.isSame(getRawBiome(x - 1, z - 1)); - edge = edge || !search.isSame(getRawBiome(x - 1, z + 1)); - edge = edge || !search.isSame(getRawBiome(x + 1, z - 1)); - edge = edge || !search.isSame(getRawBiome(x + 1, z + 1)); - - if (edge) { - biome = search.getEdge(); - } - } - - return biome; - } - - @Override - public void setChunkProcessor(TriConsumer processor) { - this.processor = processor; - } - - @Override - public BiomeChunk getChunk(int cx, int cz, boolean update) { - ChunkPos cpos = new ChunkPos(cx, cz); - SquareBiomeChunk chunk = maps.get(cpos); - if (chunk == null) { - synchronized (random) { - random.setLargeFeatureWithSalt(0, cpos.x, cpos.z, 0); - chunk = new SquareBiomeChunk(random, picker); - } - maps.put(cpos, chunk); - - if (update && processor != null) { - processor.accept(cx, cz, chunk.getSide()); - } - } - - return chunk; - } - - private BiomePicker.ActualBiome getRawBiome(double bx, double bz) { - double x = bx * size / sizeXZ; - double z = bz * size / sizeXZ; - - double px = bx * 0.2; - double pz = bz * 0.2; - - for (int i = 0; i < depth; i++) { - double nx = (x + noiseX.eval(px, pz)) / 2F; - double nz = (z + noiseZ.eval(px, pz)) / 2F; - - x = nx; - z = nz; - - px = px / 2 + i; - pz = pz / 2 + i; - } - - int ix = MHelper.floor(x); - int iz = MHelper.floor(z); - - if ((ix & SquareBiomeChunk.MASK_WIDTH) == SquareBiomeChunk.MASK_WIDTH) { - x += (iz / 2) & 1; - } - if ((iz & SquareBiomeChunk.MASK_WIDTH) == SquareBiomeChunk.MASK_WIDTH) { - z += (ix / 2) & 1; - } - - ChunkPos cpos = new ChunkPos(MHelper.floor(x / SquareBiomeChunk.WIDTH), MHelper.floor(z / SquareBiomeChunk.WIDTH)); - SquareBiomeChunk chunk = maps.get(cpos); - if (chunk == null) { - synchronized (random) { - random.setLargeFeatureWithSalt(0, cpos.x, cpos.z, 0); - chunk = new SquareBiomeChunk(random, picker); - } - maps.put(cpos, chunk); - } - - return chunk.getBiome(MHelper.floor(x), MHelper.floor(z)); - } -} diff --git a/src/main/java/ru/bclib/world/processors/DestructionStructureProcessor.java b/src/main/java/ru/bclib/world/processors/DestructionStructureProcessor.java deleted file mode 100644 index 381bd324..00000000 --- a/src/main/java/ru/bclib/world/processors/DestructionStructureProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.bclib.world.processors; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; -import ru.bclib.util.BlocksHelper; -import ru.bclib.util.MHelper; - -public class DestructionStructureProcessor extends StructureProcessor { - private int chance = 4; - - public void setChance(int chance) { - this.chance = chance; - } - - @Override - public StructureBlockInfo processBlock(LevelReader worldView, BlockPos pos, BlockPos blockPos, StructureBlockInfo structureBlockInfo, StructureBlockInfo structureBlockInfo2, StructurePlaceSettings structurePlacementData) { - if (!BlocksHelper.isInvulnerable( - structureBlockInfo2.state, - worldView, - structureBlockInfo2.pos - ) && MHelper.RANDOM.nextInt(chance) == 0) { - return null; - } - return structureBlockInfo2; - } - - @Override - protected StructureProcessorType getType() { - return StructureProcessorType.RULE; - } -} diff --git a/src/main/java/ru/bclib/world/processors/TerrainStructureProcessor.java b/src/main/java/ru/bclib/world/processors/TerrainStructureProcessor.java deleted file mode 100644 index 5753b07f..00000000 --- a/src/main/java/ru/bclib/world/processors/TerrainStructureProcessor.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.bclib.world.processors; - -import net.minecraft.core.BlockPos; -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.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; -import ru.bclib.api.biomes.BiomeAPI; - -public class TerrainStructureProcessor extends StructureProcessor { - private final Block defaultBlock; - public TerrainStructureProcessor(Block defaultBlock){ - this.defaultBlock = defaultBlock; - } - - @Override - public StructureBlockInfo processBlock(LevelReader worldView, BlockPos pos, BlockPos blockPos, StructureBlockInfo structureBlockInfo, StructureBlockInfo structureBlockInfo2, StructurePlaceSettings structurePlacementData) { - BlockPos bpos = structureBlockInfo2.pos; - if (structureBlockInfo2.state.is(defaultBlock) && worldView.isEmptyBlock(bpos.above())) { - final BlockState top = BiomeAPI.findTopMaterial(worldView.getBiome(pos)).orElse(defaultBlock.defaultBlockState()); - return new StructureBlockInfo(bpos, top, structureBlockInfo2.nbt); - } - return structureBlockInfo2; - } - - @Override - protected StructureProcessorType getType() { - return StructureProcessorType.RULE; - } -} diff --git a/src/main/java/ru/bclib/world/structures/BCLStructure.java b/src/main/java/ru/bclib/world/structures/BCLStructure.java deleted file mode 100644 index 934e8351..00000000 --- a/src/main/java/ru/bclib/world/structures/BCLStructure.java +++ /dev/null @@ -1,155 +0,0 @@ -package ru.bclib.world.structures; - -import com.google.common.collect.Lists; - -import net.minecraft.core.*; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.data.worldgen.StructureSets; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.structure.*; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType; - -import com.mojang.serialization.Codec; -import ru.bclib.api.tag.TagAPI; -import ru.bclib.mixin.common.StructuresAccessor; - -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.function.Function; - -public class BCLStructure { - private static final Random RANDOM = new Random(354); - - private final Holder structure; - private final GenerationStep.Decoration featureStep; - private final List biomes = Lists.newArrayList(); - private final ResourceLocation id; - public final TagKey biomeTag; - public final ResourceKey structureKey; - public final S baseStructure; - public final ResourceKey structureSetKey; - public final RandomSpreadStructurePlacement spreadConfig; - - public final StructureType structureType; - - public final Codec STRUCTURE_CODEC; - - - private static HolderSet biomes(TagKey tagKey) { - return BuiltinRegistries.BIOME.getOrCreateTag(tagKey); - } - private static Structure.StructureSettings structure(TagKey tagKey, Map map, GenerationStep.Decoration decoration, TerrainAdjustment terrainAdjustment) { - return new Structure.StructureSettings(biomes(tagKey), map, decoration, terrainAdjustment); - } - - private static Structure.StructureSettings structure(TagKey tagKey, GenerationStep.Decoration decoration, TerrainAdjustment terrainAdjustment) { - return structure(tagKey, Map.of(), decoration, terrainAdjustment); - } - - private static StructureType registerStructureType(ResourceLocation id, Codec codec) { - return Registry.register(Registry.STRUCTURE_TYPES, id, () -> codec); - } - public BCLStructure(ResourceLocation id, Function structureBuilder, GenerationStep.Decoration step, int spacing, int separation) { - this(id, structureBuilder, step, spacing, separation, false); - } - - public BCLStructure(ResourceLocation id, Function structureBuilder, GenerationStep.Decoration step, int spacing, int separation, boolean adaptNoise) { - this.id = id; - this.featureStep = step; - - this.STRUCTURE_CODEC = Structure.simpleCodec(structureBuilder); - //parts from vanilla for Structure generation - //public static final ResourceKey> JUNGLE_TEMPLE = - // BuiltinStructures.createKey("jungle_pyramid"); - //public static final Holder> JUNGLE_TEMPLE = - // Structures.register(BuiltinStructures.JUNGLE_TEMPLE, Structure.JUNGLE_TEMPLE.configured(NoneFeatureConfiguration.INSTANCE, BiomeTags.HAS_JUNGLE_TEMPLE)); - //public static final Holder JUNGLE_TEMPLES = - // StructureSets.register(BuiltinStructureSets.JUNGLE_TEMPLES, Structures.JUNGLE_TEMPLE, new RandomSpreadStructurePlacement(32, 8, RandomSpreadType.LINEAR, 14357619)); - //public static final Structure JUNGLE_TEMPLE = - // Structure.register("jungle_pyramid", new JunglePyramidFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES); - // - - this.spreadConfig = new RandomSpreadStructurePlacement(spacing, separation, RandomSpreadType.LINEAR, RANDOM.nextInt(8192)); - this.structureKey = ResourceKey.create(Registry.STRUCTURE_REGISTRY, id); - this.structureSetKey = ResourceKey.create(Registry.STRUCTURE_SET_REGISTRY, id); - this.structureType = registerStructureType(id, STRUCTURE_CODEC); - - this.biomeTag = TagAPI.makeBiomeTag(id.getNamespace(), "has_structure/"+id.getPath()); - this.baseStructure = structureBuilder.apply(structure(biomeTag, featureStep, TerrainAdjustment.NONE)); - this.structure = StructuresAccessor.callRegister(structureKey, this.baseStructure); - StructureSets.register(structureSetKey, this.structure, spreadConfig); - } - - /** - * runs the {@code PieceGeneratorSupplier.Context::validBiome} from the given context at - * height=5 in the middle of the chunk. - * - * @param context The context to test with. - * @return true, if this feature can spawn in the current biome - */ - public static boolean isValidBiome(Structure.GenerationContext context) { - return isValidBiome(context, 5); - } - /** - * runs the {@code PieceGeneratorSupplier.Context::validBiome} from the given context at the - * given height in the middle of the chunk. - * - * @param context The context to test with. - * @param yPos The Height to test for - * @return true, if this feature can spawn in the current biome - */ - public static boolean isValidBiome(Structure.GenerationContext context, int yPos) { - BlockPos blockPos = context.chunkPos().getMiddleBlockPosition(yPos); - return context.validBiome().test( - context - .chunkGenerator() - .getBiomeSource() - .getNoiseBiome( - QuartPos.fromBlock(blockPos.getX()), - QuartPos.fromBlock(blockPos.getY()), - QuartPos.fromBlock(blockPos.getZ()), - context.randomState().sampler() - ) - ); - } - - public Holder getStructure() { - return structure; - } - - public GenerationStep.Decoration getFeatureStep() { - return featureStep; - } - - /** - * Get the structure ID; - * @return {@link ResourceLocation} id. - */ - public ResourceLocation getID() { - return id; - } - - /** - * Adds biome into internal biome list, used in {@link ru.bclib.api.biomes.BCLBiomeBuilder}. - * @param biome {@link ResourceLocation} biome ID. - */ - public void addInternalBiome(ResourceLocation biome) { - biomes.add(biome); - } - - /** - * Get biome list where this structure feature can generate. Only represents biomes made with {@link ru.bclib.api.biomes.BCLBiomeBuilder} and only - * if structure was added during building process. Modification of this list will not affect structure generation. - * @return {@link List} of biome {@link ResourceLocation}. - */ - public List getBiomes() { - return biomes; - } -} diff --git a/src/main/java/ru/bclib/world/structures/StructureWorld.java b/src/main/java/ru/bclib/world/structures/StructureWorld.java deleted file mode 100644 index 48c2ea30..00000000 --- a/src/main/java/ru/bclib/world/structures/StructureWorld.java +++ /dev/null @@ -1,169 +0,0 @@ -package ru.bclib.world.structures; - -import com.google.common.collect.Maps; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.structure.BoundingBox; - -import java.util.Map; - -public class StructureWorld { - private Map parts = Maps.newHashMap(); - private ChunkPos lastPos; - private Part lastPart; - private int minX = Integer.MAX_VALUE; - private int minY = Integer.MAX_VALUE; - private int minZ = Integer.MAX_VALUE; - private int maxX = Integer.MIN_VALUE; - private int maxY = Integer.MIN_VALUE; - private int maxZ = Integer.MIN_VALUE; - - public StructureWorld() {} - - public StructureWorld(CompoundTag tag) { - minX = tag.getInt("minX"); - maxX = tag.getInt("maxX"); - minY = tag.getInt("minY"); - maxY = tag.getInt("maxY"); - minZ = tag.getInt("minZ"); - maxZ = tag.getInt("maxZ"); - - ListTag map = tag.getList("parts", 10); - map.forEach((element) -> { - CompoundTag compound = (CompoundTag) element; - Part part = new Part(compound); - int x = compound.getInt("x"); - int z = compound.getInt("z"); - parts.put(new ChunkPos(x, z), part); - }); - } - - public void setBlock(BlockPos pos, BlockState state) { - ChunkPos cPos = new ChunkPos(pos); - - if (cPos.equals(lastPos)) { - lastPart.addBlock(pos, state); - return; - } - - Part part = parts.get(cPos); - if (part == null) { - part = new Part(); - parts.put(cPos, part); - - if (cPos.x < minX) minX = cPos.x; - if (cPos.x > maxX) maxX = cPos.x; - if (cPos.z < minZ) minZ = cPos.z; - if (cPos.z > maxZ) maxZ = cPos.z; - } - if (pos.getY() < minY) minY = pos.getY(); - if (pos.getY() > maxY) maxY = pos.getY(); - part.addBlock(pos, state); - - lastPos = cPos; - lastPart = part; - } - - public boolean placeChunk(WorldGenLevel world, ChunkPos chunkPos) { - Part part = parts.get(chunkPos); - if (part != null) { - ChunkAccess chunk = world.getChunk(chunkPos.x, chunkPos.z); - part.placeChunk(chunk); - return true; - } - return false; - } - - public CompoundTag toBNT() { - CompoundTag tag = new CompoundTag(); - tag.putInt("minX", minX); - tag.putInt("maxX", maxX); - tag.putInt("minY", minY); - tag.putInt("maxY", maxY); - tag.putInt("minZ", minZ); - tag.putInt("maxZ", maxZ); - ListTag map = new ListTag(); - tag.put("parts", map); - parts.forEach((pos, part) -> { - map.add(part.toNBT(pos.x, pos.z)); - }); - return tag; - } - - public BoundingBox getBounds() { - if (minX == Integer.MAX_VALUE || maxX == Integer.MIN_VALUE || minZ == Integer.MAX_VALUE || maxZ == Integer.MIN_VALUE) { - return BoundingBox.infinite(); - } - return new BoundingBox(minX << 4, minY, minZ << 4, (maxX << 4) | 15, maxY, (maxZ << 4) | 15); - } - - private static final class Part { - Map blocks = Maps.newHashMap(); - - public Part() {} - - public Part(CompoundTag tag) { - ListTag map = tag.getList("blocks", 10); - ListTag map2 = tag.getList("states", 10); - BlockState[] states = new BlockState[map2.size()]; - for (int i = 0; i < states.length; i++) { - states[i] = NbtUtils.readBlockState((CompoundTag) map2.get(i)); - } - - map.forEach((element) -> { - CompoundTag block = (CompoundTag) element; - BlockPos pos = NbtUtils.readBlockPos(block.getCompound("pos")); - int stateID = block.getInt("state"); - BlockState state = stateID < states.length ? states[stateID] : Block.stateById(stateID); - blocks.put(pos, state); - }); - } - - void addBlock(BlockPos pos, BlockState state) { - BlockPos inner = new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15); - blocks.put(inner, state); - } - - void placeChunk(ChunkAccess chunk) { - blocks.forEach((pos, state) -> { - chunk.setBlockState(pos, state, false); - }); - } - - CompoundTag toNBT(int x, int z) { - CompoundTag tag = new CompoundTag(); - tag.putInt("x", x); - tag.putInt("z", z); - ListTag map = new ListTag(); - tag.put("blocks", map); - ListTag stateMap = new ListTag(); - tag.put("states", stateMap); - - int[] id = new int[1]; - Map states = Maps.newHashMap(); - - blocks.forEach((pos, state) -> { - int stateID = states.getOrDefault(states, -1); - if (stateID < 0) { - stateID = id[0]++; - states.put(state, stateID); - stateMap.add(NbtUtils.writeBlockState(state)); - } - - CompoundTag block = new CompoundTag(); - block.put("pos", NbtUtils.writeBlockPos(pos)); - block.putInt("state", stateID); - map.add(block); - }); - - return tag; - } - } -} diff --git a/src/main/java/ru/bclib/world/surface/DoubleBlockSurfaceNoiseCondition.java b/src/main/java/ru/bclib/world/surface/DoubleBlockSurfaceNoiseCondition.java deleted file mode 100644 index 73e2000b..00000000 --- a/src/main/java/ru/bclib/world/surface/DoubleBlockSurfaceNoiseCondition.java +++ /dev/null @@ -1,48 +0,0 @@ -package ru.bclib.world.surface; - -import com.mojang.serialization.Codec; -import net.minecraft.core.Registry; -import net.minecraft.util.KeyDispatchDataCodec; -import net.minecraft.world.level.levelgen.SurfaceRules; -import ru.bclib.BCLib; -import ru.bclib.api.surface.rules.SurfaceNoiseCondition; -import ru.bclib.mixin.common.SurfaceRulesContextAccessor; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.util.MHelper; - -public class DoubleBlockSurfaceNoiseCondition extends SurfaceNoiseCondition { - public static final DoubleBlockSurfaceNoiseCondition CONDITION = new DoubleBlockSurfaceNoiseCondition(0); - private static final OpenSimplexNoise NOISE = new OpenSimplexNoise(4141); - public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(Codec.DOUBLE.fieldOf("threshold").xmap(DoubleBlockSurfaceNoiseCondition::new, obj -> obj.threshold).codec()); - private final double threshold; - public DoubleBlockSurfaceNoiseCondition(double threshold){ - this.threshold = threshold; - } - - @Override - public KeyDispatchDataCodec codec() { - return CODEC; - } - - private static int lastX = Integer.MIN_VALUE; - private static int lastZ = Integer.MIN_VALUE; - private static double lastValue = 0; - - @Override - public boolean test(SurfaceRulesContextAccessor context) { - final int x = context.getBlockX(); - final int z = context.getBlockZ(); - if (lastX==x && lastZ==z) return lastValue > threshold; - - double value = NOISE.eval(x * 0.1, z * 0.1) + MHelper.randRange(-0.4, 0.4, MHelper.RANDOM_SOURCE); - - lastX=x; - lastZ=z; - lastValue=value; - return value > threshold; - } - - static { - Registry.register(Registry.CONDITION , BCLib.makeID("doubleblock_surface"), DoubleBlockSurfaceNoiseCondition.CODEC.codec()); - } -} diff --git a/src/main/resources/bclib.mixins.client.json b/src/main/resources/bclib.mixins.client.json index 31d95f29..0b74b299 100644 --- a/src/main/resources/bclib.mixins.client.json +++ b/src/main/resources/bclib.mixins.client.json @@ -1,20 +1,20 @@ { "required": true, "minVersion": "0.8", - "package": "ru.bclib.mixin.client", + "package": "org.betterx.bclib.mixin.client", "compatibilityLevel": "JAVA_17", "client": [ - "ClientRecipeBookMixin", - "SignEditScreenMixin", - "ModelManagerMixin", - "TextureAtlasMixin", - "AnvilScreenMixin", - "FogRendererMixin", - "ModelBakeryMixin", + "ClientRecipeBookMixin", + "SignEditScreenMixin", + "ModelManagerMixin", + "TextureAtlasMixin", + "AnvilScreenMixin", + "FogRendererMixin", + "ModelBakeryMixin", "CreateWorldScreenMixin", - "MinecraftMixin", - "BlockMixin", - "GameMixin" + "MinecraftMixin", + "BlockMixin", + "GameMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/resources/bclib.mixins.common.json b/src/main/resources/bclib.mixins.common.json index b07e7114..e65d98f7 100644 --- a/src/main/resources/bclib.mixins.common.json +++ b/src/main/resources/bclib.mixins.common.json @@ -1,53 +1,53 @@ { "required": true, "minVersion": "0.8", - "package": "ru.bclib.mixin.common", + "package": "org.betterx.bclib.mixin.common", "compatibilityLevel": "JAVA_17", "mixins": [ - "BiomeMixin", - "BiomeGenerationSettingsAccessor", - "shears.DiggingEnchantmentMixin", - "shears.ItemPredicateBuilderMixin", - "LayerLightSectionStorageMixin", - "NoiseBasedChunkGeneratorMixin", - "NoiseGeneratorSettingsMixin", - "SurfaceRulesContextAccessor", - "EnchantingTableBlockMixin", - "shears.TripWireBlockMixin", - "StructuresAccessor", - "MobSpawnSettingsAccessor", - "shears.BeehiveBlockMixin", - "shears.PumpkinBlockMixin", - "shears.MushroomCowMixin", - "ComposterBlockAccessor", - "PotionBrewingAccessor", - "RecipeManagerAccessor", - "shears.SnowGolemMixin", - "MinecraftServerMixin", - "NetherBiomeDataMixin", - "PistonBaseBlockMixin", - "TheEndBiomeDataMixin", - "BlockStateBaseMixin", - "ChunkGeneratorMixin", - "WorldGenRegionMixin", - "DiggerItemAccessor", - "RecipeManagerMixin", - "RecipeManagerMixin", - "ShovelItemAccessor", - "BoneMealItemMixin", - "CraftingMenuMixin", - "shears.SheepMixin", - "PortalShapeMixin", - "ServerLevelMixin", - "BiomeSourceMixin", - "AnvilBlockMixin", - "AnvilMenuMixin", - "ItemStackMixin", - "TagLoaderMixin", - "MainMixin", - "WorldPresetsBootstrapMixin", - "WorldOpenFlowsMixin", - "WorldGenPropertiesMixin" + "BiomeMixin", + "BiomeGenerationSettingsAccessor", + "shears.DiggingEnchantmentMixin", + "shears.ItemPredicateBuilderMixin", + "LayerLightSectionStorageMixin", + "NoiseBasedChunkGeneratorMixin", + "NoiseGeneratorSettingsMixin", + "SurfaceRulesContextAccessor", + "EnchantingTableBlockMixin", + "shears.TripWireBlockMixin", + "StructuresAccessor", + "MobSpawnSettingsAccessor", + "shears.BeehiveBlockMixin", + "shears.PumpkinBlockMixin", + "shears.MushroomCowMixin", + "ComposterBlockAccessor", + "PotionBrewingAccessor", + "RecipeManagerAccessor", + "shears.SnowGolemMixin", + "MinecraftServerMixin", + "NetherBiomeDataMixin", + "PistonBaseBlockMixin", + "TheEndBiomeDataMixin", + "BlockStateBaseMixin", + "ChunkGeneratorMixin", + "WorldGenRegionMixin", + "DiggerItemAccessor", + "RecipeManagerMixin", + "RecipeManagerMixin", + "ShovelItemAccessor", + "BoneMealItemMixin", + "CraftingMenuMixin", + "shears.SheepMixin", + "PortalShapeMixin", + "ServerLevelMixin", + "BiomeSourceMixin", + "AnvilBlockMixin", + "AnvilMenuMixin", + "ItemStackMixin", + "TagLoaderMixin", + "MainMixin", + "WorldPresetsBootstrapMixin", + "WorldOpenFlowsMixin", + "WorldGenPropertiesMixin" ], "injectors": { "defaultRequire": 1