
Adil Yilan
Members-
Posts
73 -
Joined
-
Last visited
Everything posted by Adil Yilan
-
After upgrade from 1.21.1 to 1.21.3, when I do gradlew runData, I get really strange error that I cannot understand: Caused by: java.lang.NullPointerException: Item id not set at java.base/java.util.Objects.requireNonNull(Objects.java:259) at TRANSFORMER/minecraft@1.21.3/net.minecraft.world.item.Item$Properties.effectiveDescriptionId(Item.java:461) at TRANSFORMER/minecraft@1.21.3/net.minecraft.world.item.Item.<init>(Item.java:111) at TRANSFORMER/uniqueweaponry@1.21.3-0.4.1/ba.minecraft.uniqueweaponry.common.item.grenade.base.BaseGrenadeItem.<init>(BaseGrenadeItem.java:22) at TRANSFORMER/uniqueweaponry@1.21.3-0.4.1/ba.minecraft.uniqueweaponry.common.item.grenade.FlashGrenadeItem.<init>(FlashGrenadeItem.java:9) at TRANSFORMER/uniqueweaponry@1.21.3-0.4.1/ba.minecraft.uniqueweaponry.common.item.GrenadeItems.lambda$0(GrenadeItems.java:22) at TRANSFORMER/forge@53.0.0/net.minecraftforge.registries.DeferredRegister$EventDispatcher.lambda$handleEvent$0(DeferredRegister.java:366) at TRANSFORMER/forge@53.0.0/net.minecraftforge.registries.RegisterEvent.register(RegisterEvent.java:59) at TRANSFORMER/forge@53.0.0/net.minecraftforge.registries.DeferredRegister$EventDispatcher.handleEvent(DeferredRegister.java:366) at TRANSFORMER/forge@53.0.0/net.minecraftforge.registries.__EventDispatcher_handleEvent_RegisterEvent.invoke(.dynamic) at SECURE-BOOTSTRAP/net.minecraftforge.eventbus/net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:48) at SECURE-BOOTSTRAP/net.minecraftforge.eventbus/net.minecraftforge.eventbus.EventBus.post(EventBus.java:304) at SECURE-BOOTSTRAP/net.minecraftforge.eventbus/net.minecraftforge.eventbus.EventBus.post(EventBus.java:290) Have there been changes related on how items should be registered now?
-
Hi, Here is the overview of the 0.4.0 update for the Unique Weaponry mod we have been working on, that adds few new weapons: Evoker's Tome Skullcaster Infernal Scepter Mod can be downloaded from CurseForge: https://www.curseforge.com/minecraft/mc-mods/unique-weaponry Any suggestions and ideas are more than welcome!
-
Hi all Just wanted to share an update to Unique Magic & Enchantments mods that adds 30+ new enchantments to Minecraft. In this update we have added two new enchantments: Kensei - Increases damage done based on XP level Bone Breaker - Has chance to dismantle skeletons when hit with arrow Mod can be downloaded on CurseForge: https://legacy.curseforge.com/minecraft/mc-mods/unique-magic Here is an overview of update: If you have any new ideas or suggestions, let us know. Thanks!
-
Hi, Here is the overview of the update for the Unique Weaponry mod we have been working on: Mod can be downloaded from CurseForge: https://www.curseforge.com/minecraft/mc-mods/unique-weaponry Any suggestions and ideas are more than welcome!
-
This is the code for my enchantment tags provider for data generation: public final class ModEnchantmentTagsProvider extends EnchantmentTagsProvider { public ModEnchantmentTagsProvider(PackOutput packOutput, CompletableFuture<Provider> lookupProvider) { super(packOutput, lookupProvider); } @Override protected void addTags(Provider provider) { tag(EnchantmentTags.ARMOR_EXCLUSIVE) .add(ArmorEnchantments.FREEZING_PROTECTION) .add(ArmorEnchantments.LIGHTNING_PROTECTION) .add(ArmorEnchantments.MAGIC_PROTECTION) .add(ArmorEnchantments.SONIC_PROTECTION); tag(ModEnchantmentTags.XP_GAIN_EXCLUSIVE) .add(Enchantments.MENDING) .add(ArmorEnchantments.EXPLORATION); } @Override public String getName() { return "Unique Magic Enchantment Tags"; } } So what am I doing here is: 1) Adding my custom protection enchantments to be ARMOR_EXCLUSIVE so they don't clash with existing protection enchantments 2) Adding tag that makes my custom EXPLORATION enchantment to be exclusive with MENDING enchantment This is then registered in ModDataGenerators: dataGen.addProvider(event.includeServer(), new ModEnchantmentTagsProvider(packOutput, lookupProvider)); When datagen is run, I end up with this error: Caused by: java.util.concurrent.CompletionException: java.lang.IllegalArgumentException: Couldn't define tag minecraft:exclusive_set/armor as it is missing following references: uniquemagic:freezing_protection,uniquemagic:lightning_protection,uniquemagic:magic_protection,uniquemagic:sonic_protection Enchantments have been migrated to 1.21 and they work in game as they did in 1.20.6. I also get enchantment JSON files in src/generated/resources so that is working well too. However, I cannot make these tags to work no matter what I try. Is there something that I am missing?
-
Recently after I update my mods to latest Forge MDK version, and update server to latest Forge, and then replace old mods versions with new ones, once when I start server I get these messages: I am not familiar with what this error is and how to resolve it. Is this something that has to be done on server or I need to add some migration code to my mods?
-
Hi, I am implementing a mod where I have to encrypt a player's secret and persist it as an NBT. I plan to expose encryption parameters as a mod config file, where default values will be randomly generated. Since Java is not my primary language of use (I come from the C# world), I am not sure what is the best/latest/recommended approach to encrypt secrets. I've found this article which seems decent: https://www.geeksforgeeks.org/java-program-to-encrypt-password-in-configuration-files/ Is this a good way to go or there is a better/recommended way to do it with Forge/Minecraft? Thanks!
-
Hi, I need to implement periodic background execution of specific code. It should run every second, and if a few conditions are not satisfied, it should broadcast a message to either a specific player or all players. What would be the safest/recommended way to do it in Forge/Minecraft? If there are links to examples of successful implementations, sharing would be much appreciated! Thanks!
-
Alright, so the key for this is to implement a factory method in the class that extends SavedData: public static SavedData.Factory<PlayersSeenSavedData> factory() { return new SavedData.Factory<>(PlayersSeenSavedData::new, PlayersSeenSavedData::load, DataFixTypes.PLAYER); } Once the factory method is implemented, it should be provided to .get method of DimensionDataStorage: PlayersSeenSavedData savedData = storage.get(PlayersSeenSavedData.factory(), SEENS_KEY); The only thing I am not sure of is usage of DataFixTypes.PLAYER. What is this used for? How to select proper DataFixType?
-
I have updated mod that has previously worked with Forge for 1.20.1 to latest version of Forge for 1.20.2, and then code that was previously compiling properly started to show unusual message: PlayerManager.java:107: error: incompatible types: Factory is not a functional interface PlayersSeenSavedData savedData = storage.get(PlayersSeenSavedData::load, SEENS_KEY); The code looks like this: private static PlayersSeenSavedData tryLoadPlayersSeenData(DimensionDataStorage storage) { // Load saved data based on the key. PlayersSeenSavedData savedData = storage.get(PlayersSeenSavedData::load, SEENS_KEY); // IF: Data was never saved before. if(savedData == null) { savedData = PlayersSeenSavedData.create(); } return savedData; } I have checked method signature for get method of DimensionDataStorage for 1.20.1 and 1.20.2 and I cannot find any difference. Any hints on what exactly has changed and why is my load method now rejected? Here is the code for load method: public static PlayersSeenSavedData load(CompoundTag compoundTag) { // Load list of NBTs from server data. ListTag listTag = compoundTag.getList(KEY, Tag.TAG_COMPOUND); // Create new empty list that will hold all data. ArrayList<PlayerSeenData> playersData = new ArrayList<PlayerSeenData>(); // Iterate through all NBTs. for(Tag tag : listTag) { // Deserialize NBT back to regular object. PlayerSeenData playerData = PlayerSeenData.deserialize((CompoundTag)tag); // Add object to array of data. playersData.add(playerData); } // Create new instance of saved data class and provide data that was loaded to it. return new PlayersSeenSavedData(playersData); }
-
Nvm, I found solution myself Here is it in case someone else needs it: ResourceKey<Level> resKey = ResourceKey.create(Registries.DIMENSION, resLoc);
-
Hi, In 1.19.2 I had this code that would give me reference to dimension based on it's name: // Get ID of resource location for dimension. String resLocId = data.getString(key + ":dim"); // Create resource location. ResourceLocation resLoc = new ResourceLocation(resLocId); // Get resource key for dimension. ResourceKey<Level> resKey = ResourceKey.create(Registry.DIMENSION_REGISTRY, resLoc); // Get reference to server where code is running. MinecraftServer server = player.getServer(); // Get reference to level that was saved with command. ServerLevel level = server.getLevel(resKey); With DIMENSION_REGISTRY being gone from Registry class, this code no longer works. What would be a proper way to get reference to a dimension (ServerLevel) based on it's resource location identifier in 1.20.1?
-
Thanks @warjort (and @Luis_ST), I have managed to make this work as expected. I have broken dropSelf method to pieces and added BlockState related code from createBeeNestDrop method, and wrapped all that in new helper method: protected void dropSelfWithBlockState(Block block, Property<?>[] properties) { CopyBlockState.Builder blockStateCopyBuilder = CopyBlockState.copyState(block); for(Property<?> property : properties) { blockStateCopyBuilder.copy(property); } LootPoolSingletonContainer.Builder<?> itemLootTableBuilder = LootItem.lootTableItem(block); itemLootTableBuilder.apply(blockStateCopyBuilder); LootPool.Builder lootPoolBuilder = LootPool.lootPool(); lootPoolBuilder.setRolls(ConstantValue.exactly(1.0F)); lootPoolBuilder.add(itemLootTableBuilder); lootPoolBuilder = applyExplosionCondition(block, lootPoolBuilder); LootTable.Builder lootTableBuilder = LootTable.lootTable(); lootTableBuilder.withPool(lootPoolBuilder); this.add(block, lootTableBuilder); } So I can now call this for all other scenarios like this: dropSelfWithBlockState(FluidBlocks.OIL_CASK.get(), new Property<?>[] { OilCaskBlock.LEVEL }); Hope others will find this useful.
-
I want to have a cask that can be filled with oil up until the certain level. public final class OilCaskBlock extends Block { public static final int MAX_LEVEL = 16; public static final IntegerProperty LEVEL = IntegerProperty.create("level", 1, MAX_LEVEL); public OilCaskBlock() { super(createProperties()); } private static Properties createProperties() { Properties properties = Properties.of(Material.METAL, MaterialColor.COLOR_LIGHT_GRAY); properties.requiresCorrectToolForDrops(); properties.strength(5, 5); properties.sound(SoundType.METAL); properties.noOcclusion(); return properties; } @Override protected void createBlockStateDefinition(Builder<Block, BlockState> builder) { builder.add(LEVEL); } } I have also created a BlockItem for this block: public final class OilCaskBlockItem extends BlockItem { public OilCaskBlockItem() { super(FluidBlocks.OIL_CASK.get(), createProperties()); } private static Properties createProperties() { Properties properties = new Properties(); properties.tab(CreativeModeTab.TAB_MISC); properties.stacksTo(1); return properties; } } So what happens now is: I fill cask to level 8 Mine it and get item in inventory When I place item back, it is no longer level 8 but level 1 instead - so it's original BlockState was not preserved in ItemStack I have managed to find this net.minecraft.world.level.storage.loot.functions.CopyBlockState class that apparently saves BlockState in CompoundTag named "BlockStateTag" and it says it is checked when block is placed. However I do not know how to leverage this class or is it the right approach to use it. This is how my loot table is defined in datagen: public final class FluidBlockLoot extends BlockLoot { @Override protected void addTables() { dropSelf(FluidBlocks.EMPTY_CASK.get()); dropSelf(FluidBlocks.OIL_CASK.get()); } @Override protected Iterable<Block> getKnownBlocks() { return FluidBlocks.REGISTRY.getEntries().stream().map(RegistryObject::get)::iterator; } } Should I read this tag manually in getPlacementState method of BlockItem class? Is there something I need to add in datagen to trigger this BlockState copying to happen automatically? What is the missing link here?
-
@vemerion I did, but still can't figure out exactly how to set these values and what is exact meaning of them. Some enchantments have these values set to very large values that I didn't see anywhere in the game. For example, BLOCK_EFFICIENCY for level 5 returns following values: getMinCost => 41 getMaxCost => 101 So it's most likely some type of range - but how and where is it used is what I don't understand... I am supposed to set these values in my enchantments to something meaningful, but don't know how.
-
[1.19.2] Check if player can mine the block
Adil Yilan replied to Adil Yilan's topic in Modder Support
I am creating enchant that will allow player to cause quake when mining which will result in several cracks in the ground. So player hits one block, and with Quake enchantment, stones, stone replaceables and ores in specific directions will also be destroyed and possibly drop loot. I don't want to allow players to mine blocks with this enchant that would otherwise be very hard to mine with enchanted tool. So if stone tool takes too much time to mine some block in the path, I might leave that block intact and find another route for crack to happen. -
[1.19.2] Check if player can mine the block
Adil Yilan replied to Adil Yilan's topic in Modder Support
@warjort Thanks for useful info. I've just found this also: "The base time in seconds is the block's hardness multiplied by 1.5 if the player can harvest the block with the current tool, or 5 if the player cannot." So I will most likely go and calculate for duration of time required to break the block by using hardness and player.hasCorrectToolForDrops(), and place threshold instead - e.g. if it is longer than 30 seconds skip action or something like that. Thanks again -
[1.19.2] Check if player can mine the block
Adil Yilan replied to Adil Yilan's topic in Modder Support
@warjort That one is for drops, but I would like to check whether it can be destroyed through mining, regardless if it will drop or not. For example, hasCorrectToolForDrops method returns false when you mine Diamond Ore Block with Stone Pickaxe - but I really want to know if player can mine it, not if it will drop diamond, so preferred result would be true. Is there a different method that just checks for ability to destroy it? -
I am implementing some custom logic in LeftClickBlock event handler and I need to determine whether player can destroy block by simple mining with tool in hand. For example, if player starts to mine the Obsidian with Stone Pickaxe, it would be convenient to have some method that would return false for ability to destroy it, based on tool/material requirements for mining of Obsidian. Is there such a method already? I have tried getDestroySpeed on ItemStack and getDigSpeed on Player, but both return a value of 4.00 so I can't make any further logic based on that... Thanks in advance
-
I've been reading about enchanting mechanics here: https://minecraft.fandom.com/wiki/Enchanting_mechanics Table says that if you have 15 bookshelves, following rules should be applied: Level range of top slot -> 2 - 10 Level range of middle slot -> 6 - 21 Level range of bottom slot -> 30 I have defined following values for minCost and maxCost for my item (just for the sake of the testing): @Override public int getMinCost(int pLevel) { return 11; } @Override public int getMaxCost(int pLevel) { return 21; } Enchantment has only 1 level. And I ended up with having enchantment shown in two slots at the same time on the enchanting table: * At top slot with cost of 8 * At middle slot with cost of 14 At first I thought you configure how much item might cost - but how did I end up with having option with cost of 8 when min value is 11? Then I thought it has to do with levels, but if top slot is 2-10, why is it shown if min value is set to 11? What exactly are these getMinCost and getMaxCost values used for? Thanks for any assistance in advance.
-
@Luis_ST I am making some enchantments which should work on players and regular mobs, but not on boss type of mobs as it would make fights oversimplified. Some enchantments are percent based, like bleed, so applying it to boss would cut a huge proportion of boss's health. So basically it is manual work - check if entity is instance of entity classes that should be immune to enchant and that's it?
-
Is there a way to easily determine whether certain entity is a boss? By boss I mean it has health bar, advanced mechanics, more HP and attack power, etc - but mostly health bar. I have compared Ender Dragon, Wither and Warden (although I don't know if it is considered a boss), but can't find any property/method that indicates boss status. Any tips on this would be appreciated.
-
[1.19.2] Event that ticks during block mining by player
Adil Yilan replied to Adil Yilan's topic in Modder Support
Alright @Luis_ST and @warjort, thanks to your great assistance and tips, I have managed to make my idea come alive. Thank you so much! Here is the working code for future reference: @EventBusSubscriber(modid = ExperimentalMod.MODID, bus = Bus.FORGE) public final class PlayerTickEventHandler { @SubscribeEvent() public static void onPlayerTick(final PlayerTickEvent event) { // IF: Event was fired on client side. if(event.side == LogicalSide.CLIENT) { return; } // Get reference to player and cast it as ServerPlayer. ServerPlayer player = (ServerPlayer)event.player; // Get game mode for player. ServerPlayerGameMode gameMode = player.gameMode; // IF: Player is not destroying block. if(!gameMode.isDestroyingBlock) { return; } // IF: It is not end of the phase. if(event.phase != Phase.END) { return; } // Calculate ticks elapsed since breaking of block has started. int ticks = gameMode.gameTicks - gameMode.destroyProgressStart; // IF: It is not whole second. if(ticks % 20 != 0) { return; } // Get reference to level and cast it to ServerLevel. ServerLevel serverLevel = (ServerLevel)player.level; // Get block state that is being destroyed. BlockState blockState = serverLevel.getBlockState(gameMode.destroyPos); // IF: It is not runic wall. if(!blockState.is(HazardousBlocks.RUNIC_WALL.get())) { return; } // Hurt the player. player.hurt(DamageSource.MAGIC, 1); } } I tried to make it clean and commented as much as possible, but if you still see something that is off / might make issues, please let me know as I am still quite new into all of this. Thanks once more!