Posted February 23, 20169 yr Hey there, I haven't done modding in a while, so I'm a bit out of shape, but I know the basics. Is it possible to create a custom block, which prevents the spawning of monsters in a certain radius around it? If someone with great knowledge of modding could help me as a beginner, I'd really appreciate it!
February 23, 20169 yr You can subscribe to LivingSpawnEvent.CheckSpawn and set the result to DENY to prevent a passive mob spawn. This isn't fired for mobs spawned from a mob spawner. You'll probably want to maintain a list of locations that have this block using World Saved Data and iterate through them to check whether any are in range to prevent the spawn in your event handler. Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 24, 20169 yr Author Okay, so I created my custom block with a texture and a recipe. Now I'm having problems creating the list of locations. I know that I can use writeToNBT and readFromNBT in order to save additional data, like an array that contains the list of my blocks and their location. But I'm not really sure how and when to use these. I guess I can use BreakEvent when I want to delete blocks from my list. Could you give me some advice how to maintain a list of locations that have this block?
February 24, 20169 yr I would use WorldSavedData for that. Everytime one of ur blocks get added safe the position in that class. Everytime one of ur blocks get removed remove it from the class. Everytime a mod gets spawned access ur WorldSavedData, iterate over all blocks and check if they are in range to prevent spawning. If there is one block to prevent the spawn, deny the spawning and break the loop
February 24, 20169 yr Author Yeah, I wanted to do exactly what you suggested right now. My problem is that I don't know how to save the position of my block when it gets added. Could you please explain that a bit more? Maybe with code examples?
February 24, 20169 yr welcome to pseudo code island, I will beyour guide public void blockAdded(Event event) { if(event.state.getBlock()==myBlock) { CustomWorldData data = CustomWorldData.get(event.world); data.addBlock(event.pos); } } public void blockRemoved(Event event) { if(event.state.getBlock()==myBlock) { CustomWorldData data = CustomWorldData.get(event.world); data.removeBlock(event.pos); } }
February 24, 20169 yr I suggest you override Block#onBlockAdded (called after a block is added to the world) and Block#breakBlock (called when a block is removed from the world, make sure you call the super method so the TileEntity [if any] is removed). Use these to add and remove the block's position to your WorldSavedData . Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 24, 20169 yr oh yeah im dumb. u only need events to modify vanilla behaviour. but still is nearly the same, just using block methods
February 24, 20169 yr Author Okay, one question: If I use onBlockAdded() in my custom block's class, will it only be called when I add my custom block, or will it be called everytime I add any block? Edit: I checked it with system message - it only activates when I add the custom block. Problem solved This is my code for the custom block so far: package com.monsterBlocker; import java.util.ArrayList; import java.util.List; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IconRegister; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.world.World; public class MBBlock extends Block { // A list of all MBBlocks and their location public List mbBlockList = new ArrayList(); public MBBlock (int id) { super(id, Material.iron); this.setUnlocalizedName(MonsterBlocker.mbBlock_unlocalizedName); this.setCreativeTab(CreativeTabs.tabBlock); this.setHardness(5F); this.setResistance(15F); this.setStepSound(Block.soundMetalFootstep); this.setLightValue(2F); } @Override @SideOnly(Side.CLIENT) public void registerIcons(IconRegister icon) { blockIcon = icon.registerIcon(MonsterBlocker.AID + ":" + "monsterBlocker"); } @Override public void onBlockAdded(World world, int x, int y, int z) { //CustomWorldData data = CustomWorldData.get(world); //data.addBlock(x, y, z); Position pos = new Position(x, y, z); mbBlockList.add(pos); } }
February 24, 20169 yr In general, instance methods will only be called if the instance was actually involved in whatever happened. This means that Block#onBlockAdded will only be called on the Block that was actually added to the world. Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 24, 20169 yr Author Thank you. So about Saving/Loading - do I create a new class which extends WorldSavedData, and override readFromNBT() and writeToNBT()? And I cannot use the ArrayList ("mbBlockList") for saving, or can I?
February 24, 20169 yr You must create a class that extends WorldSavedData and store the data in each instance of that, yes. The page I linked in my first post explains this in more detail. You can't store the list in your Block class directly, since you don't have any way to load/save the data and can't easily separate each dimension's list. Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 24, 20169 yr Fair warning: You're going to need to learn about concurrency. Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
February 24, 20169 yr Author Okay, I think I begin to understand what I have to do in order to maintain the list: I can only save IntArrays in the NBT, no ArrayLists containing objects. When a player spawns inside a dimension, the NBT will be read (x,y,z) and the ArrayList will be created. When a player adds or removes a MBBlock, the ArrayList is updated and saved to the NBT as 3 IntArrays (containing x,y,z). Am I right so far or did I miss something important?
February 24, 20169 yr you can either do it that way or create one NBTTagcompound for each blockpos in the list and save it using a NBTTagList
February 24, 20169 yr Okay, I think I begin to understand what I have to do in order to maintain the list: I can only save IntArrays in the NBT, no ArrayLists containing objects. When a player spawns inside a dimension, the NBT will be read (x,y,z) and the ArrayList will be created. When a player adds or removes a MBBlock, the ArrayList is updated and saved to the NBT as 3 IntArrays (containing x,y,z). Am I right so far or did I miss something important? You can either save each position as an int array or a compound tag. You don't write to NBT every time a block is added/removed, Minecraft will call readFromNBT and writeToNBT for you. Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 24, 20169 yr Fair warning: You're going to need to learn about concurrency. If the list is only maintained and checked on the server, won't it all be running in a single thread? Even if you needed to maintain the list on both sides, wouldn't each side have its own WorldSavedData instance(s) each only being accessed from a single thread? Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 24, 20169 yr If the list is only maintained and checked on the server, won't it all be running in a single thread? That entirely depends on how the data is being saved to disk. HOO BOY. FileIO runs on a separate thread. Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
February 25, 20169 yr Author Okay, I think it is easier to store the data as a NBTTagList of NBTTagcompounds. So I have this class "Position" public class Position { public int x; public int y; public int z; public Position(int posx, int posy, int posz){ x = posx; y = posy; z = posz; } } And I use an ArrayList in order to store the list of blocks while playing public void onBlockAdded(World world, int x, int y, int z) { Position pos = new Position(x, y, z); mbBlockList.add(pos); } So when I want to save the data to the NBT, I think this should work, right? @Override public void writeToNBT(NBTTagCompound nbt) { NBTTagList nbttaglist = new NBTTagList(); for(int i = 0; i < mbBlockList.size(); i++) { NBTTagCompound compound = new NBTTagCompound(); Position pos = (Position) mbBlockList.get(i); compound.setInteger("x", pos.x); compound.setInteger("y", pos.y); compound.setInteger("z", pos.z); nbttaglist.appendTag(compound); } } Now I have a question about the NBTTagList: how do I set the key? Because when loading I obviously need to tell the program which NBTTagList I want to load... @Override public void readFromNBT(NBTTagCompound nbt) { NBTTagList mbBlockList = nbt.getTagList("mbBlockList");
February 25, 20169 yr You need to save the NBT list tag inside the provided NBT compound tag using NBTTagCompound#setTag in writeToNBT . If you're using generics properly, you shouldn't need to cast objects from your list to Position . Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 25, 20169 yr Author You need to save the NBT list tag inside the provided NBT compound tag using NBTTagCompound#setTag in writeToNBT . Great! So this is... @Override public void writeToNBT(NBTTagCompound nbt) { NBTTagList nbttaglist = new NBTTagList(); nbt.setTag("mbBlockList", nbttaglist); If you're using generics properly, you shouldn't need to cast objects from your list to Position . When I don't cast it to Position, Eclipse displays an error: "Type mismatch: cannot convert from Object to Position" Did I do something wrong?
February 25, 20169 yr Great! So this is... Yes, that looks correct. When I don't cast it to Position, Eclipse displays an error: "Type mismatch: cannot convert from Object to Position" Did I do something wrong? Is your field a List or a List<Position> ? Always use generic types when they're available. Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
February 25, 20169 yr Author Aaah - so that's how it works. Thank you!! public List<Position> mbBlockList = new ArrayList(); Now it works
February 25, 20169 yr Author Okay, the saving/loading starts looking good. The only thing missing is the initialization when a player spawns in a dimension. I try to call onMBInit() and return the ArrayList containing the loaded BlockList, but I'm not sure how to do that. Here is the full code: package com.monsterBlocker; import java.util.ArrayList; import java.util.List; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.WorldSavedData; public class MBBlockList extends WorldSavedData { private static final String DATA_NAME = MonsterBlocker.MODID + "_BlockList"; public List<Position> mbBlockList = new ArrayList(); public MBBlockList() { super(DATA_NAME); } public MBBlockList(String s) { super(s); } public List<Position> onMBInit() { readFromNBT(nbt); return mbBlockList; } public void onMBBlockChange(List<Position> newMBBlockList) { mbBlockList = newMBBlockList; markDirty(); } @Override public void readFromNBT(NBTTagCompound nbt) { mbBlockList.clear(); NBTTagList nbttaglist = nbt.getTagList("mbBlockList"); for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound posCompound = (NBTTagCompound) nbttaglist.tagAt(i); Position pos = new Position(posCompound.getInteger("x"), posCompound.getInteger("y"), posCompound.getInteger("z")); mbBlockList.add(pos); } } @Override public void writeToNBT(NBTTagCompound nbt) { NBTTagList nbttaglist = new NBTTagList(); nbt.setTag("mbBlockList", nbttaglist); for(int i = 0; i < mbBlockList.size(); i++) { NBTTagCompound compound = new NBTTagCompound(); Position pos = mbBlockList.get(i); compound.setInteger("x", pos.x); compound.setInteger("y", pos.y); compound.setInteger("z", pos.z); nbttaglist.appendTag(compound); } } } Soo, first question: Do I call onMBInit() in preInit(FMLPreInitializationEvent event)? Second question: What do I write in onMBInit(), so that the BlockList is loaded, so that I can return it?
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.