-
Posts
77 -
Joined
-
Last visited
Everything posted by Purplicious_Cow
-
[1.16.4] [SOLVED] Config sync between client/server
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
Just to put a cap on this one in case anyone falls into this trap, I had originally been confused by the Minecraft Forge notes found in the update to the ModConfig class, where it states that Common and Client sides are not synced, meanwhile the Server side is. From https://github.com/MinecraftForge/MinecraftForge/blob/1.16.x/src/main/java/net/minecraftforge/fml/config/ModConfig.java The description below from the Forge code could use some clarification. In the end the COMMON spec was the correct one for the case I presented above, and the error I was seeing was somewhere else in my own code. public enum Type { /** * Common mod config for configuration that needs to be loaded on both environments. * Loaded on both servers and clients. * Stored in the global config directory. * Not synced. * Suffix is "-common" by default. */ COMMON, /** * Client config is for configuration affecting the ONLY client state such as graphical options. * Only loaded on the client side. * Stored in the global config directory. * Not synced. * Suffix is "-client" by default. */ CLIENT, // /** // * Player type config is configuration that is associated with a player. // * Preferences around machine states, for example. // */ // PLAYER, /** * Server type config is configuration that is associated with a server instance. * Only loaded during server startup. * Stored in a server/save specific "serverconfig" directory. * Synced to clients during connection. * Suffix is "-server" by default. */ SERVER; public String extension() { return StringUtils.toLowerCase(name()); } } -
[1.16.4] [SOLVED] Config sync between client/server
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
God, sometimes I wonder about my brain. I just tested several cases and proved your point. The bug was in my checking of the configuration value. No need for packets. Maybe another cup of coffee. -
[1.16.3] How to find Biome in BiomeLoadingEvent.
Purplicious_Cow replied to Pickle_Face5's topic in Modder Support
Look under Biome.Category, which can also be referenced in the BiomeLoadingEvent, under event.getCategory() -
[1.16.4] [SOLVED] Config sync between client/server
Purplicious_Cow posted a topic in Modder Support
I have a general question about Configs in 1.15/1.16. Is there a way to force sync Common config between server and client (where Server is the owner of record)? I have several common config values that should be controlled by the values from the server (when on server). With Common spec, however, users can change Common values client side and the server does not override them. This allows certain players to take advantage of game play by lowering difficulty or enabling features they shouldn't have in a group setting. If I use the Server type spec, this puts all of these 'protected' values into the Server config, which is actually stored within the 'Save' file on the World server. This means that players who use the mod primarily in a single player setting will have to update their config preferences every time they create a new world, which seems like an extraordinary inconvenience for single players. What is the recommended approach for solving this issue? Thank you for any guidance. -
[1.16.4] Custom Structures not generating in Nether
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
Thanks, GenElectrovise. I had the same thought, but couldn't figure out how to force DimensionSettings to use field_242736_e (for the Nether). From my understanding of the code in func_242745_a, it looks like it pulls the world correct key prior to applying final settings, but I could be wrong. I may try this again, using access transformers to see if I can force the Nether key for this particular structure. -
[1.16.4] Custom Structures not generating in Nether
Purplicious_Cow posted a topic in Modder Support
I'm having issues trying to get Custom Structures to generate in the Nether. The same structure generates correctly in the Overworld (e.g., Swamp Biome works fine), but it doesn't even fire the event generation in the Nether. I suspect it's something in the way I've done registration or with the way Biome loading works in the Nether, but after two days, I haven't been able to move the needle. Any help appreciated: package com.inventorypets.worldgen; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.inventorypets.InventoryPets; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.WorldGenRegistries; import net.minecraft.world.biome.Biome; import net.minecraft.world.gen.DimensionSettings; import net.minecraft.world.gen.GenerationStage; import net.minecraft.world.gen.feature.IFeatureConfig; import net.minecraft.world.gen.feature.NoFeatureConfig; import net.minecraft.world.gen.feature.StructureFeature; import net.minecraft.world.gen.feature.structure.IStructurePieceType; import net.minecraft.world.gen.feature.structure.Structure; import net.minecraft.world.gen.settings.DimensionStructuresSettings; import net.minecraft.world.gen.settings.StructureSeparationSettings; import net.minecraftforge.event.world.BiomeLoadingEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.RegistryObject; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; public class WorldGen { static DeferredRegister<Structure<?>> STRUCTURES = DeferredRegister.create(ForgeRegistries.STRUCTURE_FEATURES, InventoryPets.MODID); static List<Structure<?>> STRUCTURE_LIST = new ArrayList<>(); static Map<ResourceLocation, StructureSeparationSettings> STRUCTURE_SETTINGS = new HashMap<>(); static IStructurePieceType register(IStructurePieceType type, String name) { net.minecraft.util.registry.Registry.register(net.minecraft.util.registry.Registry.STRUCTURE_PIECE, new ResourceLocation(InventoryPets.MODID, name), type); return type; } static <C extends IFeatureConfig, S extends Structure<C>> StructureFeature<C, S> register(StructureFeature<C, S> feature, String name) { WorldGenRegistries.register(WorldGenRegistries.CONFIGURED_STRUCTURE_FEATURE, new ResourceLocation(InventoryPets.MODID, name), feature); return feature; } static <C extends IFeatureConfig> RegistryObject<Structure<C>> addStructure(String name, Structure<C> structure, GenerationStage.Decoration stage, StructureSeparationSettings settings) { Structure.NAME_STRUCTURE_BIMAP.put(InventoryPets.MODID + ":" + name, structure); Structure.STRUCTURE_DECORATION_STAGE_MAP.put(structure, stage); STRUCTURE_LIST.add(structure); STRUCTURE_SETTINGS.put(new ResourceLocation(InventoryPets.MODID, name), settings); if (stage == GenerationStage.Decoration.SURFACE_STRUCTURES) { Structure.field_236384_t_ = ImmutableList.<Structure<?>>builder().addAll(Structure.field_236384_t_).add(structure).build(); } return STRUCTURES.register(name, () -> structure); } public static IStructurePieceType SKY_DUNGEON_PIECE; public static IStructurePieceType SPACE_DUNGEON_PIECE; public static IStructurePieceType NETHER_DUNGEON_PIECE; public static IStructurePieceType UNDERGROUND_DUNGEON_PIECE; public static IStructurePieceType TREE_TOP_PIECE; public static IStructurePieceType SEA_CAVE_PIECE; public static RegistryObject<Structure<NoFeatureConfig>> SKY_DUNGEON_STRUCTURE = addStructure("sky_dungeon", new SkyDungeonStructure(NoFeatureConfig.field_236558_a_), GenerationStage.Decoration.RAW_GENERATION, new StructureSeparationSettings(7, 6, 1236)); public static RegistryObject<Structure<NoFeatureConfig>> SPACE_DUNGEON_STRUCTURE = addStructure("space_dungeon", new SpaceDungeonStructure(NoFeatureConfig.field_236558_a_), GenerationStage.Decoration.RAW_GENERATION, new StructureSeparationSettings(9, 5, 1337)); public static RegistryObject<Structure<NoFeatureConfig>> NETHER_DUNGEON_STRUCTURE = addStructure("nether_dungeon", new NetherDungeonStructure(NoFeatureConfig.field_236558_a_), GenerationStage.Decoration.RAW_GENERATION, new StructureSeparationSettings(6, 3, 1438)); public static RegistryObject<Structure<NoFeatureConfig>> UNDERGROUND_DUNGEON_STRUCTURE = addStructure("underground_dungeon", new UndergroundDungeonStructure(NoFeatureConfig.field_236558_a_), GenerationStage.Decoration.RAW_GENERATION, new StructureSeparationSettings(8, 6, 1539)); public static RegistryObject<Structure<NoFeatureConfig>> TREE_TOP_STRUCTURE = addStructure("tree_top", new TreeTopStructure(NoFeatureConfig.field_236558_a_), GenerationStage.Decoration.SURFACE_STRUCTURES, new StructureSeparationSettings(4, 3, 1640)); public static RegistryObject<Structure<NoFeatureConfig>> SEA_CAVE_STRUCTURE = addStructure("sea_cave", new SeaCaveStructure(NoFeatureConfig.field_236558_a_), GenerationStage.Decoration.RAW_GENERATION, new StructureSeparationSettings(8, 6, 1741)); public static StructureFeature<NoFeatureConfig, ? extends Structure<NoFeatureConfig>> SKY_DUNGEON_FEATURE; public static StructureFeature<NoFeatureConfig, ? extends Structure<NoFeatureConfig>> SPACE_DUNGEON_FEATURE; public static StructureFeature<NoFeatureConfig, ? extends Structure<NoFeatureConfig>> NETHER_DUNGEON_FEATURE; public static StructureFeature<NoFeatureConfig, ? extends Structure<NoFeatureConfig>> UNDERGROUND_DUNGEON_FEATURE; public static StructureFeature<NoFeatureConfig, ? extends Structure<NoFeatureConfig>> TREE_TOP_FEATURE; public static StructureFeature<NoFeatureConfig, ? extends Structure<NoFeatureConfig>> SEA_CAVE_FEATURE; public static void preInit() { STRUCTURES.register(FMLJavaModLoadingContext.get().getModEventBus()); } public static void init() { SKY_DUNGEON_PIECE = register(SkyDungeonStructure.Piece::new, "sky_dungeon"); SKY_DUNGEON_FEATURE = register(SKY_DUNGEON_STRUCTURE.get().withConfiguration(NoFeatureConfig.field_236559_b_), "sky_dungeon"); SPACE_DUNGEON_PIECE = register(SpaceDungeonStructure.Piece::new, "space_dungeon"); SPACE_DUNGEON_FEATURE = register(SPACE_DUNGEON_STRUCTURE.get().withConfiguration(NoFeatureConfig.field_236559_b_), "space_dungeon"); UNDERGROUND_DUNGEON_PIECE = register(UndergroundDungeonStructure.Piece::new, "underground_dungeon"); UNDERGROUND_DUNGEON_FEATURE = register(UNDERGROUND_DUNGEON_STRUCTURE.get().withConfiguration(NoFeatureConfig.field_236559_b_), "underground_dungeon"); TREE_TOP_PIECE = register(TreeTopStructure.Piece::new, "tree_top"); TREE_TOP_FEATURE = register(TREE_TOP_STRUCTURE.get().withConfiguration(NoFeatureConfig.field_236559_b_), "tree_top"); SEA_CAVE_PIECE = register(SeaCaveStructure.Piece::new, "sea_cave"); SEA_CAVE_FEATURE = register(SEA_CAVE_STRUCTURE.get().withConfiguration(NoFeatureConfig.field_236559_b_), "sea_cave"); NETHER_DUNGEON_PIECE = register(NetherDungeonStructure.Piece::new, "nether_dungeon"); NETHER_DUNGEON_FEATURE = register(NETHER_DUNGEON_STRUCTURE.get().withConfiguration(NoFeatureConfig.field_236559_b_), "nether_dungeon"); for (Structure<?> s : STRUCTURE_LIST) { ImmutableSet.of(DimensionSettings.field_242734_c, DimensionSettings.field_242736_e); DimensionStructuresSettings.field_236191_b_ = ImmutableMap.<Structure<?>, StructureSeparationSettings>builder() .putAll(DimensionStructuresSettings.field_236191_b_) .put(s, STRUCTURE_SETTINGS.get(s.getRegistryName())) .build(); DimensionSettings.field_242740_q.getStructures().field_236193_d_.put(s, STRUCTURE_SETTINGS.get(s.getRegistryName())); } } @SubscribeEvent(priority = EventPriority.HIGHEST) public void onBiomeLoad(BiomeLoadingEvent event) { event.getGeneration().withStructure(SKY_DUNGEON_FEATURE); event.getGeneration().withStructure(SPACE_DUNGEON_FEATURE); event.getGeneration().withStructure(UNDERGROUND_DUNGEON_FEATURE); if (event.getCategory() == Biome.Category.FOREST || event.getCategory() == Biome.Category.PLAINS || event.getCategory() == Biome.Category.SAVANNA || event.getCategory() == Biome.Category.TAIGA || event.getCategory() == Biome.Category.EXTREME_HILLS ) { event.getGeneration().withStructure(TREE_TOP_FEATURE); } else if (event.getCategory() == Biome.Category.OCEAN) { event.getGeneration().withStructure(SEA_CAVE_FEATURE); } else if (event.getCategory() == Biome.Category.SWAMP || event.getCategory() == Biome.Category.NETHER) { event.getGeneration().withStructure(NETHER_DUNGEON_FEATURE); } } } -
Ahhhhh! Brilliant. Thank you. I didn't realize you could create a dummy register object from another mod.
-
Thanks diesieben07. I use the getItem() method for 99.9% of cases. I had used the unlocalized name in earlier version of inventory pets to check cross-mod (e.g., identifying the bosses from OreSpawn for example, or to avoid conflicts with other mod items). The fact that the unlocalized name can change is a good reason to use getRegistryName(). Thanks both for filling in the gaps.
-
Yes, clearly the registry name, which I will change going forward (on the versions I can still do so), but that was not my question. You are hinting at a Client vs. Server issue, but not quite. This method has been in the Inventory Pets code since 1.7.10 and players have not seen issues. Is it simply a recommendation to avoid future deprecation, or is it just a performance recommendation? Was hoping for a technical answer.
-
On diesieben07's great issues and recommendations post (linked below), item #7 reads: 7. Do not use the unlocalized names for things other than displaying the name of a block/item. If you want to access the registry name for a registry entry, use IForgeRegistryEntry::getRegistryName. Can someone explain why this is an issue? I have some small bit of code doing this now, and have not experienced any issues....
-
Nice! Yes, I think I can use this. Will report back if I run into issues/questions.
-
Hello Forge friends. Long time. I'm finally updating my mods to 1.15.x (and up), and have run into an issue with one of the features in Inventory Pets, which dynamically determined which recipe set the player saw based on a config value (thereby increasing the difficulty of game by using harder to find ingredients). I see that we can no longer use the 'remove' feature from the Registry. And I see williewillus' note in his super-valuable primer that super dynamic recipes still haven't been implemented in 1.14/1.15.x (not sure if that's the same thing). The one idea I came up with was to restrict a set of recipes to be based on an advancement which is would be unlocked after checking config settings. Probably not the best experience. Am I missing another method? Thanks for any assistance. Purp
-
FYI, I went with Lycanite's solution, which is to use try/catch on all attempts to access the data. On failure, it reverts to a default state. It's not ideal, but it prevents crashing in the case there is a conflict. Example: public float getFloatFromDataManager(DataParameter<Float> key) { try { return this.getDataManager().get(key); } catch (Exception e) { return 0; } }
-
No, no, I meant, do you think it's a bad idea to use Capabilities to do the same thing that DataParameters are doing. @diesiben07, thanks for the response. Ok, so it sounds like I have to implement the data synchronization manually. For some reason, I though the entity automatically did this (writeToNBT). How would you recommend I sync manually, using network packets? Finally, I'm going to include one of my entity base classes for the Ferret, which has crashed with this error, just in case I'm doing something really dumb with this and I don't know it (distinct possibility). This one extends EntityTameable (which has its own parameters, of course) package com.animania.common.entities.rodents; import java.util.Random; import java.util.Set; import javax.annotation.Nullable; import com.animania.Animania; import com.animania.common.ModSoundEvents; import com.animania.common.capabilities.CapabilityRefs; import com.animania.common.capabilities.ICapabilityPlayer; import com.animania.common.entities.AnimalContainer; import com.animania.common.entities.AnimaniaAnimal; import com.animania.common.entities.EntityGender; import com.animania.common.entities.ISpawnable; import com.animania.common.entities.amphibians.EntityAmphibian; import com.animania.common.entities.amphibians.EntityFrogs; import com.animania.common.entities.amphibians.EntityToad; import com.animania.common.entities.chickens.EntityChickLeghorn; import com.animania.common.entities.chickens.EntityChickOrpington; import com.animania.common.entities.chickens.EntityChickPlymouthRock; import com.animania.common.entities.chickens.EntityChickRhodeIslandRed; import com.animania.common.entities.chickens.EntityChickWyandotte; import com.animania.common.entities.genericAi.EntityAnimaniaAvoidWater; import com.animania.common.entities.rodents.ai.EntityAIFerretFindFood; import com.animania.common.entities.rodents.ai.EntityAIFindWater; import com.animania.common.entities.rodents.ai.EntityAILookIdleRodent; import com.animania.common.entities.rodents.ai.EntityAIRodentEat; import com.animania.common.entities.rodents.ai.EntityAISwimmingRodents; import com.animania.common.entities.rodents.ai.EntityAITemptRodents; import com.animania.common.entities.rodents.ai.EntityAIWatchClosestFromSide; import com.animania.common.handler.ItemHandler; import com.animania.common.helper.AnimaniaHelper; import com.animania.common.items.ItemEntityEgg; import com.animania.compat.top.providers.entity.TOPInfoProviderRodent; import com.animania.config.AnimaniaConfig; import com.animania.network.client.CapSyncPacket; import com.google.common.collect.Sets; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.EntityAIAttackMelee; import net.minecraft.entity.ai.EntityAIFollowOwner; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILeapAtTarget; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIPanic; import net.minecraft.entity.ai.EntityAISit; import net.minecraft.entity.ai.EntityAIWanderAvoidWater; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.monster.EntitySilverfish; import net.minecraft.entity.passive.EntityTameable; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.init.MobEffects; import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public class EntityFerretBase extends EntityTameable implements TOPInfoProviderRodent, ISpawnable, AnimaniaAnimal { protected static final DataParameter<Boolean> FED = EntityDataManager.<Boolean>createKey(EntityFerretBase.class, DataSerializers.BOOLEAN); protected static final DataParameter<Boolean> WATERED = EntityDataManager.<Boolean>createKey(EntityFerretBase.class, DataSerializers.BOOLEAN); protected static final DataParameter<Boolean> TAMED = EntityDataManager.<Boolean>createKey(EntityFerretBase.class, DataSerializers.BOOLEAN); protected static final DataParameter<Boolean> SITTING = EntityDataManager.<Boolean>createKey(EntityFerretBase.class, DataSerializers.BOOLEAN); protected static final DataParameter<Boolean> RIDING = EntityDataManager.<Boolean>createKey(EntityFerretBase.class, DataSerializers.BOOLEAN); protected static final DataParameter<Boolean> AGE = EntityDataManager.<Boolean>createKey(EntityFerretBase.class, DataSerializers.BOOLEAN); protected static final Set<Item> TEMPTATION_ITEMS = Sets.newHashSet(new Item[] { Items.MUTTON, Items.EGG, ItemHandler.brownEgg, Items.CHICKEN, ItemHandler.rawWyandotteChicken, ItemHandler.rawRhodeIslandRedChicken, ItemHandler.rawRhodeIslandRedChicken, ItemHandler.rawOrpingtonChicken, ItemHandler.rawPrimeChicken }); protected int fedTimer; protected int wateredTimer; protected int happyTimer; protected int tamedTimer; public int blinkTimer; public int eatTimer; public EntityAIRodentEat entityAIEatGrass; protected int damageTimer; protected FerretType type; private int delayCount; public EntityFerretBase(World worldIn) { super(worldIn); this.setSize(.75F, .40F); this.stepHeight = 1.1F; this.fedTimer = AnimaniaConfig.careAndFeeding.feedTimer + this.rand.nextInt(100); this.wateredTimer = AnimaniaConfig.careAndFeeding.waterTimer + this.rand.nextInt(100); this.happyTimer = 60; this.tamedTimer = 120; this.blinkTimer = 70 + this.rand.nextInt(70); this.enablePersistence(); this.delayCount = 5; } @Override protected void initEntityAI() { this.aiSit = new EntityAISit(this); this.tasks.addTask(0, new EntityAISwimmingRodents(this)); if (!AnimaniaConfig.gameRules.ambianceMode) { this.tasks.addTask(2, new EntityAIFindWater(this, 1.0D)); this.tasks.addTask(3, new EntityAIFerretFindFood(this, 1.0D)); } this.tasks.addTask(2, this.aiSit); this.entityAIEatGrass = new EntityAIRodentEat(this); this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.2F)); this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, true)); this.tasks.addTask(6, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F)); this.tasks.addTask(7, new EntityAIPanic(this, 1.5D)); this.tasks.addTask(8, new EntityAIRodentEat(this)); this.tasks.addTask(9, new EntityAITemptRodents(this, 1.2D, false, EntityFerretBase.TEMPTATION_ITEMS)); this.tasks.addTask(10, this.entityAIEatGrass); this.tasks.addTask(11, new EntityAIWanderAvoidWater(this, 1.2D)); this.tasks.addTask(12, new EntityAIWatchClosestFromSide(this, EntityPlayer.class, 6.0F)); this.tasks.addTask(13, new EntityAILookIdleRodent(this)); this.tasks.addTask(14, new EntityAnimaniaAvoidWater(this)); if (AnimaniaConfig.gameRules.animalsCanAttackOthers) { this.targetTasks.addTask(1, new EntityAINearestAttackableTarget(this, EntityChickLeghorn.class, false)); this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityChickOrpington.class, false)); this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityChickPlymouthRock.class, false)); this.targetTasks.addTask(4, new EntityAINearestAttackableTarget(this, EntityChickRhodeIslandRed.class, false)); this.targetTasks.addTask(5, new EntityAINearestAttackableTarget(this, EntityChickWyandotte.class, false)); this.targetTasks.addTask(6, new EntityAINearestAttackableTarget(this, EntitySilverfish.class, false)); this.targetTasks.addTask(7, new EntityAINearestAttackableTarget(this, EntityFrogs.class, false)); this.targetTasks.addTask(8, new EntityAINearestAttackableTarget(this, EntityToad.class, false)); this.targetTasks.addTask(9, new EntityAIHurtByTarget(this, true, new Class[0])); } } @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(8.0D); this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.35D); this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(0.5D); } @Override protected boolean canDespawn() { return false; } @Override public int getVerticalFaceSpeed() { return this.isSitting() ? 20 : super.getVerticalFaceSpeed(); } @Override protected void consumeItemFromStack(EntityPlayer player, ItemStack stack) { this.setFed(true); this.setOwnerId(player.getPersistentID()); this.setIsTamed(true); this.setTamed(true); this.setSitting(false); this.setFerretSitting(false); this.entityAIEatGrass.startExecuting(); if (!player.capabilities.isCreativeMode) if (stack != ItemStack.EMPTY) stack.setCount(stack.getCount() - 1); this.setInLove(player); } @Override public void setInLove(EntityPlayer player) { this.world.setEntityState(this, (byte) 18); } @Override protected void updateAITasks() { this.eatTimer = this.entityAIEatGrass.getEatingGrassTimer(); super.updateAITasks(); } @Override protected void dropFewItems(boolean hit, int lootlevel) { int happyDrops = 0; if (this.getWatered()) happyDrops++; if (this.getFed()) happyDrops++; ItemStack dropItem; if (AnimaniaConfig.drops.customMobDrops) { String drop = AnimaniaConfig.drops.ferretDrop; dropItem = AnimaniaHelper.getItem(drop); } else dropItem = null; ItemStack dropItem2; String drop2 = AnimaniaConfig.drops.ferretDrop2; dropItem2 = AnimaniaHelper.getItem(drop2); if (happyDrops == 2) { if (dropItem != null) { dropItem.setCount(1 + lootlevel); EntityItem entityitem = new EntityItem(this.world, this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, dropItem); world.spawnEntity(entityitem); } if (dropItem2 != null) { this.dropItem(dropItem2.getItem(), AnimaniaConfig.drops.ferretDrop2Amount + lootlevel); } } else if (happyDrops == 1) { if (dropItem != null) { dropItem.setCount(1 + lootlevel); EntityItem entityitem = new EntityItem(this.world, this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, dropItem); world.spawnEntity(entityitem); } if (dropItem2 != null) { this.dropItem(dropItem2.getItem(), AnimaniaConfig.drops.ferretDrop2Amount + lootlevel); } } } @Override public boolean processInteract(EntityPlayer player, EnumHand hand) { ItemStack stack = player.getHeldItem(hand); EntityPlayer entityplayer = player; if (stack != ItemStack.EMPTY && stack.getItem() == Items.WATER_BUCKET && delayCount == 0) { delayCount = 5; this.setWatered(true); this.setInLove(player); return true; } else if (stack == ItemStack.EMPTY && this.isTamed() && !this.isFerretSitting() && !player.isSneaking() && delayCount == 0) { delayCount = 5; this.setFerretSitting(true); this.setSitting(true); this.isJumping = false; this.navigator.clearPathEntity(); return true; } else if (stack == ItemStack.EMPTY && this.isTamed() && this.isFerretSitting() && !player.isSneaking() && delayCount == 0) { delayCount = 5; this.setFerretSitting(false); this.setSitting(false); this.isJumping = false; this.navigator.clearPathEntity(); return true; } else if (stack == ItemStack.EMPTY && this.isTamed() && player.isSneaking() && delayCount == 0) { delayCount = 5; ICapabilityPlayer props = CapabilityRefs.getPlayerCaps(player); if (!props.isCarrying()) { props.setAnimal(this.writeToNBT(new NBTTagCompound())); props.setCarrying(true); props.setType(EntityList.getKey(this).getResourcePath()); this.setDead(); player.swingArm(EnumHand.MAIN_HAND); Animania.network.sendToAllAround(new CapSyncPacket(props, player.getEntityId()), new NetworkRegistry.TargetPoint(player.world.provider.getDimension(), player.getPosition().getX(), player.getPosition().getY(), player.getPosition().getZ(), 64)); return true; } } return super.processInteract(player, hand); } @Override public boolean attackEntityAsMob(Entity entityIn) { boolean flag = entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), 1.0F); if (flag) this.applyEnchantments(this, entityIn); if (entityIn instanceof EntityAmphibian) { this.setFed(true); } // Custom Knockback if (entityIn instanceof EntityPlayer) ((EntityLivingBase) entityIn).knockBack(this, 1, this.posX - entityIn.posX, this.posZ - entityIn.posZ); return flag; } @Override protected void entityInit() { super.entityInit(); this.dataManager.register(EntityFerretBase.FED, Boolean.valueOf(true)); this.dataManager.register(EntityFerretBase.WATERED, Boolean.valueOf(true)); this.dataManager.register(EntityFerretBase.TAMED, Boolean.valueOf(false)); this.dataManager.register(EntityFerretBase.SITTING, Boolean.valueOf(false)); this.dataManager.register(EntityFerretBase.RIDING, Boolean.valueOf(false)); this.dataManager.register(EntityFerretBase.AGE, Boolean.valueOf(false)); } @Override public void writeEntityToNBT(NBTTagCompound compound) { super.writeEntityToNBT(compound); compound.setBoolean("Fed", this.getFed()); compound.setBoolean("Watered", this.getWatered()); compound.setBoolean("IsTamed", this.getIsTamed()); compound.setBoolean("IsSitting", this.isFerretSitting()); compound.setBoolean("Riding", this.isFerretRiding()); compound.setBoolean("Age", this.getAge()); } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ @Override public void readEntityFromNBT(NBTTagCompound compound) { super.readEntityFromNBT(compound); this.setFed(compound.getBoolean("Fed")); this.setWatered(compound.getBoolean("Watered")); this.setIsTamed(compound.getBoolean("IsTamed")); this.setFerretSitting(compound.getBoolean("IsSitting")); this.setFerretRiding(compound.getBoolean("Riding")); this.setAge(compound.getBoolean("Age")); } public boolean getAge() { return this.dataManager.get(EntityFerretBase.AGE).booleanValue(); } public void setAge(boolean age) { this.dataManager.set(EntityFerretBase.AGE, Boolean.valueOf(age)); } @Override public boolean canBeLeashedTo(EntityPlayer player) { return true; } @Override protected SoundEvent getAmbientSound() { int happy = 0; int num = 1; if (this.getWatered()) happy++; if (this.getFed()) happy++; if (happy == 2) num = 10; else if (happy == 1) num = 20; else num = 40; Random rand = new Random(); int chooser = rand.nextInt(num); if (chooser == 0) return ModSoundEvents.ferretLiving1; else if (chooser == 1) return ModSoundEvents.ferretLiving2; else if (chooser == 2) return ModSoundEvents.ferretLiving3; else if (chooser == 3) return ModSoundEvents.ferretLiving4; else if (chooser == 4) return ModSoundEvents.ferretLiving5; else if (chooser == 5) return ModSoundEvents.ferretLiving6; else return null; } @Override protected SoundEvent getHurtSound(DamageSource source) { Random rand = new Random(); int chooser = rand.nextInt(3); if (chooser == 0) return ModSoundEvents.ferretHurt1; else if (chooser == 1) return ModSoundEvents.ferretHurt1; else return null; } @Override protected SoundEvent getDeathSound() { return ModSoundEvents.ferretHurt1; } @Override public void playLivingSound() { SoundEvent soundevent = this.getAmbientSound(); if (soundevent != null) this.playSound(soundevent, this.getSoundVolume() - .3F, this.getSoundPitch()); } @Override protected void playStepSound(BlockPos pos, Block blockIn) { this.playSound(SoundEvents.ENTITY_WOLF_STEP, 0.02F, 1.5F); } private boolean interactRide(EntityPlayer entityplayer) { this.isRemoteMountEntity(entityplayer); return true; } private void isRemoteMountEntity(Entity par1Entity) { if (this.isFerretRiding()) { this.setFerretRiding(true); this.startRiding(par1Entity); } else if (!this.isFerretRiding()) this.dismountRidingEntity(); } @Override public void onLivingUpdate() { if (!this.getAge()) { this.setAge(true); } delayCount--; if (delayCount <= 0) { delayCount = 0; } if (this.isFerretSitting() || this.isRiding()) { if (this.getRidingEntity() != null) this.rotationYaw = this.getRidingEntity().rotationYaw; this.navigator.clearPathEntity(); this.navigator.setSpeed(0); } if (this.world.isRemote) this.eatTimer = Math.max(0, this.eatTimer - 1); if (this.blinkTimer > -1) { this.blinkTimer--; if (this.blinkTimer == 0) this.blinkTimer = 100 + this.rand.nextInt(100); } if (this.fedTimer > -1 && !AnimaniaConfig.gameRules.ambianceMode) { this.fedTimer--; if (this.fedTimer == 0) this.setFed(false); } if (this.wateredTimer > -1) { this.wateredTimer--; if (this.wateredTimer == 0 && !AnimaniaConfig.gameRules.ambianceMode) this.setWatered(false); } boolean fed = this.getFed(); boolean watered = this.getWatered(); if (!fed && !watered) { this.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 2, 1, false, false)); if (AnimaniaConfig.gameRules.animalsStarve) { if (this.damageTimer >= AnimaniaConfig.careAndFeeding.starvationTimer) { this.attackEntityFrom(DamageSource.STARVE, 4f); this.damageTimer = 0; } this.damageTimer++; } } else if (!fed || !watered) this.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 2, 0, false, false)); if (this.happyTimer > -1) { this.happyTimer--; if (this.happyTimer == 0) { this.happyTimer = 60; if (!this.getFed() && !this.getWatered() && AnimaniaConfig.gameRules.showUnhappyParticles) { double d = this.rand.nextGaussian() * 0.001D; double d1 = this.rand.nextGaussian() * 0.001D; double d2 = this.rand.nextGaussian() * 0.001D; this.world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + this.rand.nextFloat() * this.width - this.width, this.posY + 1.5D + this.rand.nextFloat() * this.height, this.posZ + this.rand.nextFloat() * this.width - this.width, d, d1, d2); } } } if (this.tamedTimer > -1) { this.tamedTimer--; if (this.tamedTimer == 0) { this.tamedTimer = 120; if (this.getIsTamed() && AnimaniaConfig.gameRules.showUnhappyParticles) { double d = this.rand.nextGaussian() * 0.02D; double d1 = this.rand.nextGaussian() * 0.02D; double d2 = this.rand.nextGaussian() * 0.02D; this.world.spawnParticle(EnumParticleTypes.HEART, this.posX + this.rand.nextFloat() * this.width - this.width, this.posY + 1D + this.rand.nextFloat() * this.height, this.posZ + this.rand.nextFloat() * this.width - this.width, d, d1, d2); } } } super.onLivingUpdate(); } @Override @SideOnly(Side.CLIENT) public void handleStatusUpdate(byte id) { if (id == 10) this.eatTimer = 80; else super.handleStatusUpdate(id); } public boolean isFerretSitting() { return this.dataManager.get(EntityFerretBase.SITTING).booleanValue(); } public void setFerretSitting(boolean flag) { if (flag) this.dataManager.set(EntityFerretBase.SITTING, Boolean.valueOf(true)); else this.dataManager.set(EntityFerretBase.SITTING, Boolean.valueOf(false)); } public boolean isFerretRiding() { return this.dataManager.get(EntityFerretBase.RIDING).booleanValue(); } public void setFerretRiding(boolean flag) { if (flag) this.dataManager.set(EntityFerretBase.RIDING, Boolean.valueOf(true)); else this.dataManager.set(EntityFerretBase.RIDING, Boolean.valueOf(false)); } public boolean getFed() { return this.dataManager.get(EntityFerretBase.FED).booleanValue(); } public void setFed(boolean fed) { if (fed) { this.dataManager.set(EntityFerretBase.FED, Boolean.valueOf(true)); this.fedTimer = AnimaniaConfig.careAndFeeding.feedTimer + this.rand.nextInt(100); this.setHealth(this.getHealth() + 1.0F); } else this.dataManager.set(EntityFerretBase.FED, Boolean.valueOf(false)); } public boolean getWatered() { return this.dataManager.get(EntityFerretBase.WATERED).booleanValue(); } public void setWatered(boolean watered) { if (watered) { this.dataManager.set(EntityFerretBase.WATERED, Boolean.valueOf(true)); this.wateredTimer = AnimaniaConfig.careAndFeeding.waterTimer + this.rand.nextInt(100); } else this.dataManager.set(EntityFerretBase.WATERED, Boolean.valueOf(false)); } public boolean getIsTamed() { return this.dataManager.get(EntityFerretBase.TAMED).booleanValue(); } public void setIsTamed(boolean fed) { if (fed) this.dataManager.set(EntityFerretBase.TAMED, Boolean.valueOf(true)); else this.dataManager.set(EntityFerretBase.TAMED, Boolean.valueOf(false)); } @SideOnly(Side.CLIENT) public float getHeadRotationPointY(float p_70894_1_) { return this.eatTimer <= 0 ? 0.0F : this.eatTimer >= 4 && this.eatTimer <= 176 ? 1.0F : this.eatTimer < 4 ? (this.eatTimer - p_70894_1_) / 4.0F : -(this.eatTimer - 80 - p_70894_1_) / 4.0F; } @SideOnly(Side.CLIENT) public float getHeadRotationAngleX(float p_70890_1_) { if (this.eatTimer > 4 && this.eatTimer <= 176) { float f = (this.eatTimer - 4 - p_70890_1_) / 24.0F; return (float) Math.PI / 5F + (float) Math.PI * 7F / 150F * MathHelper.sin(f * 28.7F); } else return this.eatTimer > 0 ? (float) Math.PI / 5F : this.rotationPitch * 0.017453292F; } @Override public EntityFerretBase createChild(EntityAgeable ageable) { return null; } @Override public boolean isBreedingItem(@Nullable ItemStack stack) { return stack != ItemStack.EMPTY && EntityFerretBase.TEMPTATION_ITEMS.contains(stack.getItem()); } @Override public Item getSpawnEgg() { return ItemEntityEgg.ANIMAL_EGGS.get(new AnimalContainer(this.type, EntityGender.NONE)); } @Override public ItemStack getPickedResult(RayTraceResult target) { return new ItemStack(getSpawnEgg()); } @Override public int getPrimaryEggColor() { // TODO Auto-generated method stub return 0; } @Override public int getSecondaryEggColor() { // TODO Auto-generated method stub return 0; } @Override public EntityGender getEntityGender() { return EntityGender.NONE; } }
-
Thanks, diesieben07, But I'm not going to do that for modpackers with hundreds of mods. So, to be clear... So you don't recommend using capabilities? Also.. based on your response... what if I am extending some vanilla entities (for instance EntityCow) ... could that be a source of conflict if other mods do the same and add their parameters to an extended class?
-
Hey Team Forge - I have a question, very similar to this thread at the very bottom of this post answered back in July '17. I have seen crashes like the below reported on Animania reported since its launch 9 months ago. It does appear to be "heap pollution", as you suspect, as it only occurs in multi-mod environments (where at least one of the mods adds other custom entities) and in a server environment. There is nothing in my code that would or could even change the stored value from an Integer to a Boolean (that's just weird). Based on all evidence it is externally created, and can happen to any animal and any parameter. Example crash: https://github.com/capnkirok/animania/files/1652984/crash-2018-01-22_12.33.17-client.txt However, I have no idea of identifying which mods are causing the problem. I saw another discussion about this elsewhere where Vazkii chimed in and said the only real way avoid the issue altogether is to move from Data Parameters to Capabilities. Any suggestions here? I would love to be able to error trap it, but I can't even do that (unless you know a way). Or would you recommend moving to Capabilities? Thx, Purp
-
Hey Forgers. I've just discovered a bug in Animania where when I register the Universal Bucket for one the cows' milks, it registers all Universal Buckets (e.g., slop, or liquids from other mods) into that category "listAllmilk" in OreDictionary. Here's the relevant code ItemStack milkHolstein = UniversalBucket.getFilledBucket(ForgeModContainer.getInstance().universalBucket, BlockHandler.fluidMilkHolstein); OreDictionary.registerOre("listAllmilk", milkHolstein); I know UniversalBucket.getFilledBucket is deprecated, but that does not seem to be the issue. Even trying FluidUtils.getFilledBucket with a proper FluidStack results in the same outcome from OreDictionary. Is this because UniversalBucket is using NBT? Is there a way around this?
-
BiomeDictionary causing client chunk ticking...?
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
Thanks, Lex. Yes, I saw that post on reddit and used it to change my world gen. Good stuff. Your analysis is correct. I did some more testing and there are more opportunities for custom worldgen to occur, which is causing additional client time, as you suggest. Thanks for the tip. Yeah, I was using "contains" for others in the list... lazy coder's biome dictionary. Working fine now. -
Hello modding Forge friends - I've been experimenting with BiomeDictionary recently, for WorldGen as well as for Entity Spawns. Today, doing some local testing on Inventory Pets, and noticed that when I started using BiomeDictionary for WorldGen I started seeing client chunk ticking. There's nothing special in my implementation. AFTER Biome biomegenbase = world.getBiome(new BlockPos(Xcoord, 60, Zcoord)); if(BiomeDictionary.hasType(biomegenbase, Type.FOREST)) { int Ycoord = 60 + random.nextInt(60); new WorldGenTreeTop(false).generate(world, random, new BlockPos(Xcoord, Ycoord, Zcoord)); } BEFORE (no ticking) Biome biomegenbase = world.getBiome(new BlockPos(Xcoord, 60, Zcoord)); if(biomegenbase.getBiomeName().equals("Forest")) { int Ycoord = 60 + random.nextInt(60); new WorldGenTreeTop(false).generate(world, random, new BlockPos(Xcoord, Ycoord, Zcoord)); } But this seemed to cause more frequent chunk loading infos in the Console. Question: Does BiomeDictionary come with the same caveats as OreDictionary? In other words, use it sparingly and don't use it in tick loops?
-
[1.10.2] Structure Gen / Flower Pots
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
Thanks once again, Choonster. So, it acts like a Chest now, basically. Thanks for the tip on metadata. I see it is deprecated anyway, so I will update. Edit: Yep, works now. That was quite easy. Was staring at the BlockFlowerPot class but completely missed the TileEntity. -
Random question. I can't see to get filled Flower Pots to work in structure gen code I'm updating from 1.7.10 to 1.10. Other blocks that have meta data have no issues (e.g., Beds, Chests). But for some reason Pots always come up empty, no matter what I set the meta to. this.setBlock(world, i + 3, j + 6, k + 2, Blocks.FLOWER_POT, 11); public void setBlock(World world, int x, int y, int z, Block block, int metadata) { world.setBlockState(new BlockPos(x, y, z), (IBlockState) block.getStateFromMeta(metadata)); } My guess is that it has something to do with 'CONTENTS' but my trail has run cold at this point.
-
[1.10.2] How to get ModId from Item
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
Thanks Choonster! -
[1.10.2] How to get ModId from Item
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
Yes, that will work. I'm used to parsing these strings by now, so I am ok with the 'hackiness'. Thanks for the suggestion. -
[1.10.2] How to get ModId from Item
Purplicious_Cow replied to Purplicious_Cow's topic in Modder Support
No, I know my own ModId, ha ha I'm talking about getting the ModId from another mod's item. The code above provides that in 1.7.10. I'm looking for a way to do it in 1.10.2 -
In 1.710, I had this nifty piece of code that could pull the ModId from an item. In 1.10.2, GameRegistry has changed, and with it, it seems, is the path to get ModId. Can anyone point me in the correct direction? I use the modid for various purposes. 1.7.10 code that worked: public static String getModId(Item item) { GameRegistry.UniqueIdentifier id = GameRegistry.findUniqueIdentifierFor(item); return id == null || id.modId.equals("") ? "minecraft" : id.modId; } public static String getModId(ItemStack key) { return getModId(key.getItem()); }