jabelar
Members-
Posts
3266 -
Joined
-
Last visited
-
Days Won
39
Everything posted by jabelar
-
You should never need to call those functions. You should just need to markDirty(). If you look in my example, the whole point is that all the methods that can change data also do the markDirty(). You need to have the data in (or directly accessible from) the class that extends WorldSaveData, and then you should only change the data through that class -- you'll notice that I have methods in my data class that are used to access the data within the contained area classes -- you should not change the area information directly. So, as long as: 1) your world data is properly appended to the vanilla world data with the set method 2) you only change data through the world data class 3) you mark dirty every time there is a change. Then you should be good. Once you have that working, the next topic is to sync to client!
-
This is exactly the type of thread I envisioned when I was complaining on the pull request about the complexity of the new registration system... we went from needing a single, easy-to-understand line of code to register an entity to complex set of entries, builders and such. I'm still at a bit of a loss to understand the advantage of the new system. diesieben07 do you understand the advantage / reason that this sort of complexity is needed? I suppose it has something to do with the fact that entities need to be instantiated where other things that are registered (items, blocks, recipes) are singletons?
-
Depending on what you're trying to achieve you might be able to create a custom fluid block or maybe a block that extends the liquid block that acts like water, but it would likely be a lot of work. Also, the key point would remain the same -- you need to place a source block. However, it might be worth a try -- create a custom block that extends BlockWater and change the behavior of the source so it could work at a reduced level.
-
I did. I wouldn't have asked here if I hadn't. That's how I found the information on the two formats. I need to try the MCCreator tool, since I haven't done that recently. It's quite possible it has an option now to save in the alternate format. I actually think it may be pretty easy to simply edit by hand too. I've got a few NBT viewer/editors and it would only be about 10 minutes work to move things around to match the new format.
-
Okay, I was playing with this a bit, coding it my own way and I have a few tips and suggestions. First of all, the world saved data class should also contain the fields that you're interested in changing. Those fields can in fact be simply a list of instances of another class, but the point is that you should be doing the converting between NBT read from file and the class field values. So the way I would do it is that the world saved data class will contain a list of the protected area instances. But the world saved data class should also be used to set the data in those area instances because you need to know when they change to markDirty() properly. Furthermore you have to develop the writeToNBT() and readFromNBT() methods. They should basically use tag lists to hold the list of areas which in turn hold the list of blocks within the area. So for example, here is my protected area class example (note that I only stored name and list of block positions, in your code you should add other things like tenant, taxes and such): public class ProtectedArea { private String name; private List<BlockPos> listBlocks = new ArrayList<BlockPos>(); /** * Instantiates a new protected area. * * @param parName the par name */ public ProtectedArea(String parName) { name = parName; } /** * Gets the name. * * @return the name */ public String getName() { return name; } /** * Adds the block position to the protected blocks list. * * @param parPos the par pos */ public void addBlock(BlockPos parPos) { listBlocks.add(parPos); } /** * Removes the block position from the protected blocks list. * * @param parPos the par pos */ public void removeBlock(BlockPos parPos) { listBlocks.remove(parPos); } /** * Clears the protected blocks. */ public void clearBlocks() { listBlocks.clear(); } /** * Gets the protected block list. * * @return the protected block list */ public List<BlockPos> getProtectedBlockList() { return listBlocks; } /** * Gets the block list tag. * * @return the block list tag */ public NBTTagList getBlockListTag() { NBTTagList tagList = new NBTTagList(); Iterator<BlockPos> iterator = listBlocks.iterator(); while (iterator.hasNext()) { BlockPos pos = iterator.next(); NBTTagCompound posCompound = new NBTTagCompound(); posCompound.setInteger("x", pos.getX()); posCompound.setInteger("y", pos.getY()); posCompound.setInteger("z", pos.getZ()); tagList.appendTag(posCompound); } return tagList; } } And my example of the class that extends WorldSavedData is: public class ProtectedAreaData extends WorldSavedData { private static final String DATA_NAME = MainMod.MODID + "_data_aree"; private List<ProtectedArea> listAreas = new ArrayList<ProtectedArea>(); /** * Instantiates a new protected area data. */ public ProtectedAreaData() { super(DATA_NAME); } /** * Instantiates a new protected area data. * * @param name the name */ public ProtectedAreaData(String name) { super(name); } /** * Gets the world saved data instance associated to a given world. * * @param world the world * @return the data instance */ public static ProtectedAreaData getDataInstance(World world) { MapStorage storage = world.getMapStorage(); ProtectedAreaData instance = (ProtectedAreaData) storage.getOrLoadData(ProtectedAreaData.class, DATA_NAME); if (instance == null) { instance = new ProtectedAreaData(); storage.setData(DATA_NAME, instance); } return instance; } /** * Adds the area to the list of protected areas. * * @param parArea the par area */ public void addArea(ProtectedArea parArea) { listAreas.add(parArea); markDirty(); } /** * Removes the area from the list of protected areas. * * @param parArea the par area */ public void removeArea(ProtectedArea parArea) { listAreas.remove(parArea); markDirty(); } /** * Clear the protected areas list. */ public void clearAreas() { listAreas.clear(); markDirty(); } /** * Gets the area by name. * * @param parName the par name * @return the area by name */ @Nullable public ProtectedArea getAreaByName(String parName) { Iterator<ProtectedArea> iterator = listAreas.iterator(); while (iterator.hasNext()) { ProtectedArea area = iterator.next(); if (area.getName().equals(parName)) { return area; } } return new ProtectedArea(parName); } /** * Adds the block to a given area. * * @param parName the par name * @param parPos the par pos */ public void addBlockToArea(String parName, BlockPos parPos) { getAreaByName(parName).addBlock(parPos); markDirty(); } /** * Removes the block from a given area. * * @param parName the par name * @param parPos the par pos */ public void removeBlockFromArea(String parName, BlockPos parPos) { getAreaByName(parName).removeBlock(parPos); markDirty(); } /** * Clear blocks from area. * * @param parName the par name */ public void clearBlocksFromArea(String parName) { getAreaByName(parName).clearBlocks(); markDirty(); } /** * Checks if a block position is protected. * * @param parPos the par pos * @return true, if is block pos protected */ public boolean isBlockPosProtected(BlockPos parPos) { Iterator<ProtectedArea> iteratorArea = listAreas.iterator(); while (iteratorArea.hasNext()) { ProtectedArea area = iteratorArea.next(); if (area.getProtectedBlockList().contains(parPos)) { return true; } } return false; } /* (non-Javadoc) * @see net.minecraft.world.storage.WorldSavedData#readFromNBT(net.minecraft.nbt.NBTTagCompound) */ //load @Override public void readFromNBT(NBTTagCompound nbt) { listAreas.clear(); NBTTagList tagListAreas = nbt.getTagList("Protected Areas", 10); // 10 indicates a list of NBTTagCompound Iterator<NBTBase> iterator = tagListAreas.iterator(); while (iterator.hasNext()) { NBTTagCompound areaCompound = (NBTTagCompound) iterator.next(); ProtectedArea area = new ProtectedArea(areaCompound.getString(areaCompound.getString("Area Name"))); listAreas.add(area); NBTTagList tagListPos = areaCompound.getTagList("Block List", 10); Iterator<NBTBase> iterator2 = tagListPos.iterator(); while (iterator2.hasNext()) { NBTTagCompound posCompound = (NBTTagCompound) iterator2.next(); BlockPos pos = new BlockPos( posCompound.getInteger("x"), posCompound.getInteger("y"), posCompound.getInteger("z") ); area.addBlock(pos); } } } /* (non-Javadoc) * @see net.minecraft.world.storage.WorldSavedData#writeToNBT(net.minecraft.nbt.NBTTagCompound) */ //save @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { NBTTagList tagList = new NBTTagList(); // cycle through the list of areas Iterator<ProtectedArea> iteratorArea = listAreas.iterator(); while (iteratorArea.hasNext()) { NBTTagCompound tagCompound = new NBTTagCompound(); ProtectedArea area = iteratorArea.next(); tagCompound.setString("Area Name", area.getName()); tagCompound.setTag("Block List", area.getBlockListTag()); tagList.appendTag(tagCompound); } nbt.setTag("Protected Areas", tagList); return nbt; } } NOTE: I have not tested this code yet. But I think the general approach should work well. It is very similar to the world data implemented for the ItemMap. Now, when the player is changing things in your GUI you can use the methods to add the blocks and such as you need. In summary, I think you need to have the informations in fields that are in the world save data class and you should furthermore implement the writeToNBT() and readFromNBT() to suit the information you want to save. For syncing the client, you will have to send the world data yourself as a custom packet. The good news is that the minecraft packet can accept NBT directly as a payload. I hope that helps. I need to go to be as it is past midnight ...
-
Hey, always nice to see new people getting into modding. Also, it is really nice to see that you actually tried something before asking -- there are so many lazy people who want to get all the answers without learning for themselves. So based on the code you posted, I have a few suggestions. For the exception, the description of the problem is pretty clear. The method isItemEqual() needs an ItemStack instance passed to it, but you passed an Item. This brings up a few things I want to teach you: 1) In Minecraft some classes are "singletons" meaning there is only one instance of them in the whole game. Each item (also block) is actually a singleton. They appear to be multiple in game because each item is handled in an ItemStack which associates the single Item instance with a quantity as well as data that is unique to that stack (damage/meta data as well as NBT data). For blocks there is actually a different system where a map where every block position (BlockPos) has a block state (IBlockState) that associates the single block instance with data that is unique to that block location (meta data). The concept of which things in Minecraft are singletons is important and impacts how you compare things (see my tutorial on which comparison methods to use here http://jabelarminecraft.blogspot.com/p/minecraft-modding-singletons-instances.html). 2) In modding we're working with other people's code and sometimes it is disorganized or misleading. In this case, the method name talks about the item so it might be tempting to think you should pass an item, but you must look closely at the functionality of every method (use the Open Declaration feature in Eclipse to find the code for the method) to make sure it does what you think it does. 3) You need to use Eclipse to help you avoid errors, and it can even help you by giving you possible fixes. In this case Eclipse should have warned you that the parameter didn't match. With all that being said, you need to pass an item stack to the method. It is okay to create a temporary ItemStack for the purpose of the test so you can go isItemEqual(new ItemStack(Items.IRON_PICKAXE)) to fix the problem. Alternatively, you could get the item from the active item stack and compare it. Now regarding your plan to "create an event', you're thinking about it wrong. There is already events available for most things modders might want to do, so what you want to do is "subscribe an event handling method". I have a tutorial on event handling here: http://jabelarminecraft.blogspot.com/p/minecraft-forge-172-event-handling.html In any case you should look at subscribing to events like BlockEvent.BreakEvent which are fired when a block is broken. There is also a harvest event, and I think there is tool use events as well.
-
[1.12] Block isn't updating on the client, but only sometimes
jabelar replied to Dream's topic in Modder Support
I'm not sure why it isn't working for you, but if you're stuck an alternative would be you can make Block B have a tile entity. -
(Unsolved) 1.11.2 | How the heck to make a multiblock
jabelar replied to Daring Do's topic in Modder Support
The world getEntitiesWithinAABBExcludingEntity() method can take an entity selector parameter to help filter the results. In any case, all the related methods return a list and you can simply go through that list and remove any entities that don't match your requirements. -
(Unsolved) 1.11.2 | How the heck to make a multiblock
jabelar replied to Daring Do's topic in Modder Support
Yeah, by compression I mean any scheme where you reduce the actual data stored. So having an "A" to represent all the cobblestone blocks saves space instead of saving "minecraft:cobblestone" every time. I think both world class and maybe player class have methods for getEntitiesInAABB() sort of thing. The exact names may be different, and some can exclude things (like you probably want to exclude the player), but just look at the type hierarchy for the world and player classes. Or you could look at the Structure Block code and copy how it does it. -
So I know that Structure Blocks and related classes like Template use the .nbt format for the structure assets. But of course, there are a lot of 3rd party tools and cool examples of structures that use the .schematic format. They both seem to be NBT based, but it looks like they are not the same thing: https://minecraft.gamepedia.com/Structure_block_file_format and https://minecraft.gamepedia.com/Schematic_file_format In my recent coding I plan to use .nbt but since I have a lot of old structures in schematic format and also may still find it easier to build and save things in third party tools, what is the easiest way to convert? I suppose I can always build the schematic one and capture/save again with a structure block, but I'm assuming maybe the third party tools now allow saving in either format, or that there is a converter someone has already done?
-
(Unsolved) 1.11.2 | How the heck to make a multiblock
jabelar replied to Daring Do's topic in Modder Support
Well the term "schematic" just really means a specification indicating how something is built. There were 3rd party tools like MCCreator that used a file format with file extension = .schematic which was NBT-based. However, the Structure Block introduced another type of "schematic" file with file extension = .nbt which is also NBT-based but different. So that is probably what your friend meant by "schematics" no longer being used -- he means that .schematic files aren't be used but other types of "schematics" are now becoming mainstream. Like anything in coding there are many ways to solve a problem. For example, the array I explained above is pretty easy but technically if your structure is "sparse" (mostly air) then it could be more efficient to simply have a list with each block type and relative position. Or you could create a compressed array where every bit represents a location. My main point is that as long as you figure out a way to identify block positions, it is all good. -
[1.12] Block isn't updating on the client, but only sometimes
jabelar replied to Dream's topic in Modder Support
Well, BlockStairs does override this method. I guess you're right that the actual functionality might not be in there, but they still thought (or at some point during development in the past thought) that it would be a useful method for stairs. Anyway, my point is that this is a commonly used method for detecting walking on. There are many examples, magma, slime, redstone ore, and so forth that do actual functionality within that method. -
[1.12] Block isn't updating on the client, but only sometimes
jabelar replied to Dream's topic in Modder Support
Are you doing the tick just for detecting the entity, or also doing other stuff? I think that the problem is probably that you're missing it somehow -- the tick processing is only happening about once per second so maybe the player is already past the block sometimes? But if the server processes it it is still strange that it is not synced. Maybe the chunk needs to be updated or something... However, I would suggest doing it another way. The Block class already has an onEntityWalk() method. I think you can forget the tick stuff and just have your block change in that method. The method will give you the entity type so you can check for if it is a player or not. This method is what is used to make stairs work, and to bounce on slime, and so forth. -
(Unsolved) 1.11.2 | How the heck to make a multiblock
jabelar replied to Daring Do's topic in Modder Support
If you think about it, the array I mentioned above really is a "schematic". You can of course do it in some JSON file or other format, but if all you're doing is checking for relative positions of simple blocks, the array is a very quick way. You can pretty much just "draw" the pattern in the array. There is nothing magical about a "schematic", it is just any method for specifying locations of blocks. -
(Unsolved) 1.11.2 | How the heck to make a multiblock
jabelar replied to Daring Do's topic in Modder Support
Since the pattern is symmetrical, it should be pretty easy to detect. Simply in the code (or event handler) for right-clicking on the blue block you would simply look in all the relative locations for the expected orange wool and if any one block is missing consider it a fail to match. So assuming you have the center_x, center_y, center_z location (from the location of the blue block) you'd do something like this (this is psuedo code, you'll have to implement in Java the same logic): if block that is two blocks east and two blocks north is not orange wool then return false; if block that is three blocks east and two blocks north is not orange wool then return false; .... check all the positions. return true; // it will only get to this line if all the block positions checked above were orange_wool Hopefully you get the idea. If you want to get clever it might be possible to use loops but even if you don't use loops, the logic is simple and it isn't that much typing because you can cut & past most of it. Sometimes simply using "brute force" is a valid way -- no need to create complicated JSON files, Template classes, and such if you can just use some simple Java to do the same. Actually, to make it simpler I would probably create an array with the pattern, like an array that is initialized like to contain the data like: [[ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0 ], [ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ], ... and similar for the full pattern. Then you can loop through the array with only a couple lines of code. You could then easily test for other patterns by just having other arrays. Hope that helps. -
I have tutorial on capabilities here as well: http://jabelarminecraft.blogspot.com/p/minecraft-17x.html
-
(Unsolved) 1.11.2 | How the heck to make a multiblock
jabelar replied to Daring Do's topic in Modder Support
Well, if it is an actual structure then you can use the chunk providers' isInsideStructure() and getNearestStructure() methods to help you. If you just need a pattern of blocks, then you just have to write the code to look for the pattern. If the pattern is relatively simple, that shouldn't be too hard although you'll have to consider the various rotations possible and check for them all. -
You use the tag compound's setTag() method to add other NBTBase objects, including NBTList to the compound. So in your case if you take the NBTCompound (like the world data compound or you can create a new NBTTagCompound depending on what exactly you're doing at the time.) and go setTag("area_claim_list", getNBTListArea()) it should add it to the compound.
-
It looks kinda correct to me. One thing is that the addSpawnBiome() already does the provider.allowedBiomes.add() and that might be a problem because then it is listed twice. Not really sure that is a problem, but it might be as the addSpawnBiome() specifically won't add it if it has already been added. So you should definitely delete the line with the BiomeProvider. Also, I've also seen mention that maybe you also need to use the addStrongholdBiome() and addVillageBiome() methods although I suspect you only need one of the three -- you certainly shouldn't have to have your biome be a place where village or stronghold should generate, but worth a try. Also, you should probably put a System.out.println() statement in your registration code to confirm it is indeed firing. Here's a tip from Choonster on another thread: To maybe help you figure out if the biome is actually generating or not: BiomeProvider#findBiomePosition will try to find the position of one of the specified biomes in a fixed range around the starting coordinates and return null if none was found. Note that this uses the biomes that would currently generate at each position rather than the biomes that have already generated at each position. This is an important distinction if the world was generated with a different set of biomes to those currently registered. To check for biomes that have already generated, use World#getBiomeGenForCoords. BlockPos.getAllInBoxMutable can be used to create an Iterable<BlockPos> that iterates through every BlockPos between two positions.
-
Sorry you're right, but I got confused because you register your class to the bus in pre-init and most people have switched to the @EventBusHandler annotation instead so admit I just assumed you did it all the old way. Like I said, maybe subscribe to the other events, including maybe the terrain gen bus, related to world generation and chunk population and print out console statements to see if your biome is ever actually used. Also possibly in your ashBlock in the getDefaultState() method maybe put a console statement to see how much activity it is getting in terms of being placed. Or if that generates too much console spam, set a breakpoint on getDefaultState() for your block (you might have to @Override and just call the super-method in order to breakpoint within your custom block.)
-
[Solved]Letting Client Player know It Needs to read NBT
jabelar replied to jredfox's topic in Modder Support
The thing you have to understand is that people who are attracted to programming are often very precise, meticulous people. I work with many engineers who can't get past terms that are imprecise. So while I could understand what you were trying to say, I also understand why the way you were describing the problem would bother many programmers. You were mixing the ideas of the data itself with the intermediate format used by the serialization, you were mixing concept of caching with the concept of any data in memory, and you were mixing the concept of client saving data with the client communicating with the server to save data. Some people simply find it hard to work with loosely applied terminology. So please don't resent diesieben07. His multiple attempts to clarify were actually his way of showing he cared, was trying to listen, and wanted to help. It's just that he has a very precise mind and is most effective at helping when the problem is framed precisely. -
How do you know it is not working? It is actually a bit tough to know if your biome is working without wandering around a lot in the world, but you should at least be able to tell that your biome is registered and you could do things like handling the world gen or chunk load events and test what biome is present. Also note that in 1.12.x you should technically register your biomes by subscribing to the RegistryEvent<Biome> instead. See information on that here: https://mcforge.readthedocs.io/en/latest/concepts/registries/ However I think that for now at least that registering in pre-init should still work.
-
First of all, we can't really tell if your code is correct. That is up to you. The general approach looks correct but you have to verify the details. Have you tried to run the code? Did it work? If you read the documentation it already explains how the saving works. It happens automatically as part of extending the world data class and using the setData() method. To test your code you simply need to add console statements to follow the execution. If you put console statements in the right places you will be able to easily determine whether the code runs, when it runs, and the value of any key fields during the execution.
-
I think both of your approaches won't work. Minecraft is built to prevent cheating. That means that the client doesn't get to decide what can be reached otherwise everyone would just put a client mod. So it is up to the server to determine the reach distance. The client can think it reached something but then the next time the server updates the client it will disallow it and things will get glitchy. So what you need to do is have the server process an alternate reach distance. I have a tutorial on how to do it here: http://jabelarminecraft.blogspot.com/p/minecraft-modding-extending-reach-of.html