jabelar
Members-
Posts
3266 -
Joined
-
Last visited
-
Days Won
39
Everything posted by jabelar
-
[1.11.2] [SOLVED] How and when can capabilities be null?
jabelar replied to Daeruin's topic in Modder Support
Well a warning can simply be that -- a warning. It is not necessarily an error. A warning just means you should check to make sure it is really what you want. If you don't want those warnings, it may be a matter of adding the @Nullable annotations appropriately. Or otherwise, maybe setting up the warning preferences in your IDE. -
Trouble getting entity AI to function properly
jabelar replied to ghostwolf_'s topic in Modder Support
You may want to look at the entity join world event. I can't remember but the position may be set at that point. But yeah there is a potential for position not being set at the time of the constructor unless you explicitly set it. -
[1.12.1] How do i add custom models to my items
jabelar replied to dragonwhisper92's topic in Modder Support
The "common" proxy thing is something that set in many modders' psyche early on and honestly I continue to perpetuate it although as diesieben07 rightly mentions it is a misleading way to implement things. The confusion occurred because in many modding situations the sided concerns are mostly related to the client-only stuff rather than the server side stuff. However, the annotation was intended to also have a server side so some early modder came up with an example that used inheritance where the server proxy only contained common stuff and the client inherited from that and extended it with the client-side stuff. Everyone else copied from that person and this scheme has been used by lots of people since. I should probably update my tutorials and example mods accordingly. It's just that very few people seem to run into cases where they have server-only stuff to manage and so lumping server as "common" has worked for most. -
Trouble getting entity AI to function properly
jabelar replied to ghostwolf_'s topic in Modder Support
Honestly, to figure this sort of stuff out you should practice just looking at the vanilla code -- that's how I come up with my answers and tutorials. Just takes a couple minutes usually and will make you more independent and confident in your modding, especially since you are already comfortable with Java. I took a quick look and it looks like EntityCreature is the level in the class type hierarchy where the homePosition and maximumHomeDistance fields are added. So if your entity extends EntityCreature (or subclass thereof) you already have that available. Otherwise, if you've extended EntityLiving or other higher class you'll have to recreate the functionality. If you look at the call hierarchy for homePosition, it is used by some AI such as EntityAIMoveToBlock. So you might want to add that AI to your entity, or create a custom one that does something similar. Note to make sure you set the home position and home distance with the setHomePosAndDistance() method, as I don't think it is automatically set (it might be though, I didn't look too hard at the code). In the end, a "home" position is just a block pos stored in your entity, so probably easy to make up your own mechanism. But check out how other entities use the homePosition and maximumHomeDistance fields for ideas. -
Trouble getting entity AI to function properly
jabelar replied to ghostwolf_'s topic in Modder Support
So obviously when partially full there will be a conflict here, both will be true. Regarding the AI, I don't think priority alone controls this -- it works together with the idea of "compatible" and "interruptible". Let's look at the code. First we have the onUpdateTasks() method: public void onUpdateTasks() { ArrayList arraylist = new ArrayList(); Iterator iterator; EntityAITasks.EntityAITaskEntry entityaitaskentry; if (this.tickCount++ % this.tickRate == 0) { iterator = this.taskEntries.iterator(); while (iterator.hasNext()) { entityaitaskentry = (EntityAITasks.EntityAITaskEntry)iterator.next(); boolean flag = this.executingTaskEntries.contains(entityaitaskentry); if (flag) { if (this.canUse(entityaitaskentry) && this.canContinue(entityaitaskentry)) { continue; } entityaitaskentry.action.resetTask(); this.executingTaskEntries.remove(entityaitaskentry); } if (this.canUse(entityaitaskentry) && entityaitaskentry.action.shouldExecute()) { arraylist.add(entityaitaskentry); this.executingTaskEntries.add(entityaitaskentry); } } } else { iterator = this.executingTaskEntries.iterator(); while (iterator.hasNext()) { entityaitaskentry = (EntityAITasks.EntityAITaskEntry)iterator.next(); if (!entityaitaskentry.action.continueExecuting()) { entityaitaskentry.action.resetTask(); iterator.remove(); } } } You can see that the iteration continues whether each task continues or starts, however there is a test for canUse(). Let's look at that more closely. Here is the code for canUse(): /** * Determine if a specific AI Task can be executed, which means that all running higher (= lower int value) priority * tasks are compatible with it or all lower priority tasks can be interrupted. */ private boolean canUse(EntityAITasks.EntityAITaskEntry p_75775_1_) { this.theProfiler.startSection("canUse"); Iterator iterator = this.taskEntries.iterator(); while (iterator.hasNext()) { EntityAITasks.EntityAITaskEntry entityaitaskentry = (EntityAITasks.EntityAITaskEntry)iterator.next(); if (entityaitaskentry != p_75775_1_) { if (p_75775_1_.priority >= entityaitaskentry.priority) { if (this.executingTaskEntries.contains(entityaitaskentry) && !this.areTasksCompatible(p_75775_1_, entityaitaskentry)) { this.theProfiler.endSection(); return false; } } else if (this.executingTaskEntries.contains(entityaitaskentry) && !entityaitaskentry.action.isInterruptible()) { this.theProfiler.endSection(); return false; } } } this.theProfiler.endSection(); return true; } So you see the key is that depending on the priority relationship, the tasks are checked for whether they are compatible and/or iterruptible. So let's finally look at the areTasksCompatible() and isInterruptible() methods: /** * Returns whether two EntityAITaskEntries can be executed concurrently */ private boolean areTasksCompatible(EntityAITasks.EntityAITaskEntry p_75777_1_, EntityAITasks.EntityAITaskEntry p_75777_2_) { return (p_75777_1_.action.getMutexBits() & p_75777_2_.action.getMutexBits()) == 0; } /** * Determine if this AI Task is interruptible by a higher (= lower value) priority task. All vanilla AITask have * this value set to true. */ public boolean isInterruptible() { return true; } To make a task so it cannot be interrupted, you need to override it and return false. That might be enough to fix your issue, although technically there may be cases you want it to be interruptible (like what if entity is attacked or something?) For compatibility, they use mutex (mutually exclusive) bits. Basically this is a common computer science technique the essentially uses bit flags to indicate what can run at the same time. In Minecraft AI, this is generally used for categories of AI -- for example you wouldn't want both wandering and sitting to happen at the same time or the same thing you've been experiencing would happen. So the main point is that priority alone doesn't stop other AI from happening. You either have to adjust the mutex bits to make them mutually exclusive or you have to make the uninterruptible. By the way I have a tutorial on all this here: http://jabelarminecraft.blogspot.com/p/minecraft-forge-1721710-custom-entity-ai.html -
[1.7.10] Client crash on attempt to access World.isRemote
jabelar replied to tezuni's topic in Modder Support
That can't be due to sided issues because isRemote is intended specifically to be used on both sides. But what do you mean that the debugger says there is no isRemote member? You shouldn't even be able to compile or run code if the class doesn't have a member. Is Eclipse warning you in the source code that there is a problem? Did you choose to run with errors or something? If Eclipse is warning you about error then maybe you did the wrong import for the World class. -
[1.12] Getting the correct player movement speed in blocks per second
jabelar replied to Prickles's topic in Modder Support
You're still confused about how servers and clients work, as well as how packet handling works. There is no such thing as mc on the server. Assuming you mean mc as Minecraft.getMinecraft() that only exists on the client. You're also trying to send a packet from the RenderOverlayEvent which is fired on the client, so that is wrong. Anyway, you're making it too complicated. You simply need to calculate the player speed on the server in some tick event and send a packet from there. Wherever you have a player, you also have the world so use that. There are actually several options for where you choose to execute the code. For example, you could do it on a WorldTick event in which case you would of course have a world available. Or you could do it on a PlayerTick event where you would have world via the player. Here again are the steps you need to do, with more emphasis on which side the code runs on: On the server, in the world tick handler, confirm it is running on the server side (with !world.isRemote) and if so then: 1) do a for loop through all the players and store the UUID and the speed (calculated) in a map type field. 2) send a message to all that you construct by passing the hashmap in as a parameter. Your toBytes() method should first put an int that indicates the size of the map, then write the UUID, then the speed, for each entry in the map. 3) on the client you should have a global field with same map format (UUID and speed). On the client, in your message handler which is registered to client side: 4) your fromBytes() should first read the int for the number of player entries, and then do that many loops to read the UUID and speed pairs to construct a hashmap that you store in a global field (not in the message class but somewhere else static like your main class). 5) in your rendering event handler you should find list of all players you care about (I guess you want the ones nearby) and then look up their speed based on their UUID from your map and do the rendering you want. -
[1.11] Get Held Item from EntityLivingBase not working
jabelar replied to MSpace-Dev's topic in Modder Support
== ALWAUYS looks to see if the things being compared are the actual same instance. Not just equivalent but actually the same thing stored in same memory. So when you compared to a new itemstack it will always be false because that is a different instance located in different place in memory. equals() MAY do something more and is intended to be used to test for equivalence. But needs to be overridden as apppropriate for the class. Usually an implementation will check each field in the instance to see if each of them are equivalent (which may require further calls to sub fields depending until you get down to comparing primitives. You can use == for singletons. For example, in Minecraft Item instances are singletons (there is only one instance of each in the game ). But Itemstack is not a singleton (each stack is its own instance) so you would only use == in very specific circumstances. When using equals() it is good idea to check out the implementation to confirm it does what you expect. However, in the case of ItemStacks there is actually a separate set of methods for comparing because you might only be interested in certain aspects -- like do you care if the durability is the same, or the NBT tags, etc. So you should consider the following methods: areItemStacksEqual() areItemStacksEqualUsingNBTShareTag() areItemStackShareTagsEqual() areItemStackTagsEqual() areItemsEqual() areItemsEqualIgnoreDurability() -
[1.12] Getting the correct player movement speed in blocks per second
jabelar replied to Prickles's topic in Modder Support
Okay, you're getting closer. Your constructor for the packet is wrong though. The packet is supposed to be constructed on the server and sent to the client. However, in your constructor you're using the Minecraft class which is client-side only. Instead it would be better if you passed a world instance as a parameter into the packet constructor and then got the entity list from that, and then when you instantiate a packet pass a the world from the server. However, I think the null pointer is probably another problem. It seems that maybe the network channel isn't set up properly? Do you have a properly registered network channel to send the packet on? -
Still didn't give us any information so we can help you. You need to answer my questions. What did you try for debugging? Did you confirm the AI is being called? Which path of your code is being executed? Is it a problem with swimming or with wandering?
-
I've got some tutorial information as well as links to others here: http://jabelarminecraft.blogspot.com/p/minecraft-17x.html Note that part of the confusion is that there are actually four fairly different situations related to capabilities: Using existing capabilities (e.g. IItemHolder) in your custom classes Using custom capabilities in your custom classes Using existing capabilities on vanilla (or other mod's) classes Using custom capabilities on vanilla (or other mod's) classes I think people get confused because often a tutorial will be covering a case different than yours and so if you apply it you'll either be missing something needed or will duplicate something unnecessary.
-
[1.12.2] Strange case of java.lang.NoClassDefFoundError on server
jabelar replied to Aarilight's topic in Modder Support
If you're not using Eclipse, what is your development environment? -
Look for block from entity in very large radius [256x256x256]
jabelar replied to MSpace-Dev's topic in Modder Support
This is some good stuff from diesieben07. @MSpace-Devto summarize, instead of looking 256 blocks in each direction you'll look 16 chunks in each direction (i.e. your main loop). For each chunk cooordinates (calculated by shifting the bits in their corner block's position as mentioned by diesieben07), you use the world's getChunkFromChunkCoordinates() method to get the chunk, and then use the getTileEntityMap() method on that chunk, then do whatever you need to with the tile entities in the map. By the way, if your code is still lagging after all this you can consider distributing the execution over several ticks. In most cases things don't have to all happen together in exactly the same tick, so for example you could process all chunks in one direction in one tick, all in in another direction in the next tick, etc. and then repeat. One other thing to consider is that 16 chunks away is a pretty big distance. There is a reason why Minecraft stops tracking entities at a distance, only loads chunks when needed, and basically limits the scope of what needs to be processed. While it is nice to consider mod features that process the "whole" world, there are practical considerations. Since the performance workload grows as the square of the radius. So just reducing the radius a bit will greatly improve performance. So you if you have problem at 16 chunks, try 15, 14, etc. -
Well, what have you done to debug it? Does it have trouble moving both in water and out of water? Have you confirmed whether the wander AI (or swimming if in water) is being called correctly? You can set breakpoints and run Eclipse in debug mode to check. Have you confirmed whether the moveEntityWithHeading() method is being called correctly? You can set breakpoints and run Eclipse in debug mode, or you can add console statements (System.out.println()) to check.
-
[1.12.2] Strange case of java.lang.NoClassDefFoundError on server
jabelar replied to Aarilight's topic in Modder Support
Yeah, it took me a while to realize that a modder should pretty much always test with a dedicated server as the client run configuration hides all this sided stuff, so that makes sense while this was lurking while you got things well developed... So if it is only happening when running as server I guess it probably is related to sided class loading. In terms of debugging it, sometimes errors are thrown in a way that doesn't correctly identify the root cause. Like maybe the calling class has an issue. But I have some suggestions. First of all, it seems to be having trouble on the line where it instantiates a new NBTHelper. So why not try commenting out everything except the constructor in the NBTHelper class and see if it fixes the issue. If it does, then add the methods back one at a time until a problem occurs. If the issue isn't fixed by minimizing the code in the class, then that would be odd but you could try looking at class loader inspection. Others on this forum might be more familiar with the class loader, but generally you might be able to inspect it using standard techniques in Eclipse: https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Ftasks%2Fanalyzingclassloader.html In general the error means that the class is not loaded (into memory) for the JVM (in this case the server JVM). I think you can also set the run configuration vm to be more verbose in the console and perhaps more class loading info might show up. -
[1.12.2] Strange case of java.lang.NoClassDefFoundError on server
jabelar replied to Aarilight's topic in Modder Support
Is this code yours originally, or are you contributing to someone else's code? I'm asking because the code is fairly complex and seems to be fully implemented, yet errors like this are sort of fundamental so it is surprising you're finding this out now. If this is your own code, then I just want to pass on advice to say that it is important when doing a big mod to test every bit at a time to make sure it is working well before continuing to add code, otherwise you end up with this sort of thing (where you have tons of code but weird, fundamental bugs). In particular, the NBT stuff is a bit convoluted. I understand sometimes it is nice to encapsulate things with helpers, but in this case it is instantiating a builder that gets associated to a compound and redirecting almost everything you might want to do with NBT through methods that access that compound. Not sure that complexity is really necessary or worth it. Anyway, it is true that there isn't anything obvious that would be related to sided issues. So maybe it is related to some other reason the class wouldn't be found. Like is there something wrong with the package or class path? -
Well, the next step in debugging is to trace the execution to confirm where it is failing to act as expected. So I use console print statements (System.out.println()) although you can also use debugging mode or other techniques for visibility during execution. You'll want to ensure that each method is being called as you expect, so at the beginning of each put in a print statement. Then for any conditional statements (like if statements) I usually put a statement before to print out the values of the fields that will be tested and I also put a print statement in each path of the execution to confirm what it decides to do. With that visibility into the execution it is usually very quickly obvious what is going wrong. The method may not even be called (maybe the AI is failing to cause an actual attack), or maybe you'll find some other problem. For debugging entity AI, I often make may own copies of the AI classes that include print statements in the same manner as I describe above. Then it all becomes very clear. Maybe it isn't targeting, etc.
-
[1.12.2] Strange case of java.lang.NoClassDefFoundError on server
jabelar replied to Aarilight's topic in Modder Support
Stuff that is client side annotated has no meaning on the server. You can use sided annotation on specific fields and methods in your class. So if you have methods that reference client-side stuff you can annotate that method. So just put sided annotation on your registerModels() method and anything that calls it. -
You usually need to override the attackEntityAsMob() method as well as the attackEntityFrom(), and possibly the canAttackClass() and the attackable() methods. These are inherited from EntityLivingBase and such, but for EntityCreature (which you have extended) they are not fully implemented.
-
Okay, so after digging around for a bit, it is a bit tricky to intercept block gen after everything has been populated. The most efficient place is where the ChunkPrimer is directly accessible, but the replaceBiomeBlocks event is too early for many types of surface biome-based blocks -- the event seems to be intended for entirely replacing the generation. I think I'll file an issue and maybe a pull request to allow the ChunkPrimer be availabile in most gen events and also ensure there is an event that is fired just before the ChunkPrimer is copied into the Chunk thereby allowing editing after everything else is complete. In any case, it seems that the most consistent place where you have access to all the blocks after they are freshly created is the ChunkEvent.Load event which is called both after generation as well as actual loading. So the following example worked for me -- for fun I replaced all grass with slime blocks: public static Block fromBlock = Blocks.GRASS; // change this to suit your need public static Block toBlock = Blocks.SLIME_BLOCK; // change this to suit your need @SubscribeEvent(priority=EventPriority.NORMAL, receiveCanceled=true) public static void onEvent(ChunkEvent.Load event) { Chunk theChunk = event.getChunk(); // replace all blocks of a type with another block type for (int x = 0; x < 16; ++x) { for (int z = 0; z < 16; ++z) { for (int y = theChunk.getHeightValue(x, z)-20; y < theChunk.getHeightValue(x, z)+1; ++y) { if (theChunk.getBlockState(x, y, z).getBlock() == fromBlock) { theChunk.setBlockState(new BlockPos(x, y, z), toBlock.getDefaultState()); } } } } theChunk.markDirty(); } How deep you go from the top block is up to you. For replacing grass I just needed to find the surface blocks, but I found some cases where grass would be under a floating island or other overhang and so technically wasn't the top block. If you were replacing ores for example you'd want to go deeper and such. I didn't notice any lag, but I've got a decent computer. For very specific cases, there are other events that are better. But in the generic case it seems that currently the load event is best.
-
Deleted.
-
[1.9-1.10] Overriding universal bucket of custom liquid texutre
jabelar replied to Eastonium's topic in Modder Support
Well, the word "possible" is a strong word. Of course you could change it with enough work, but I'm not aware of any easy Forge hook for it. The idea with the current implementation is that it mainly is intended to show the fluid color. You may want to create a pull request for Forge to add the feature. -
May be deleted for uselessness, but this path isn't working
jabelar replied to Zane49er's topic in Modder Support
This kind of code has worked for me before. In this case I was reading in an array of blocks that had integer positions and the unlocalized block names. The size of the file was indicated by the first three integers which defined the size of the array, but you may need to do your own end-of-file detection depending on your file format. The main thing I wanted to show was how I created the buffered reader. : public void readArrays(String parName) { try { System.out.println("Reading file = "+parName+".txt"); readIn = new BufferedReader(new InputStreamReader(getClass().getClassLoader() .getResourceAsStream("assets/magicbeans/structures/"+parName+".txt"), "UTF-8")); dimX = Integer.valueOf(readIn.readLine()); dimY = Integer.valueOf(readIn.readLine()); dimZ = Integer.valueOf(readIn.readLine()); blockNameArray = new String[dimX][dimY][dimZ]; blockMetaArray = new int[dimX][dimY][dimZ]; System.out.println("Dimensions of structure = "+dimX+", "+dimY+", "+dimZ); for (int indY = 0; indY < dimY; indY++) // Y first to organize in vertical layers { for (int indX = 0; indX < dimX; indX++) { for (int indZ = 0; indZ < dimZ; indZ++) { blockNameArray[indX][indY][indZ] = readIn.readLine(); blockMetaArray[indX][indY][indZ] = Integer.valueOf(readIn.readLine()); } } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { readIn.close(); } catch (IOException e) { e.printStackTrace(); } } -
Sorry, you couldn't have done that fully or it would be obvious where the problem happens. As Choonster mentions, onBlockActivated() probably isn't even being called -- which would be obvious in the console statements (since they wouldn't print when you expect them to). Also, your if statements are still not that simple as there are conditions besides the sneaking that need to be satisfied. The thing with debugging is you have to double-check all your assumptions. Of course you expect everything is working because you wrote the code intending for it to work. So you literally have to confirm that every step of code executes exactly as you expect. You need to confirm that the method is called when you expect, that the parameter values are what you expect, that the if statement executes as you expect, and so forth.
-
I agree. Do you have any insight into the two issues I mentioned above? Seems like the storage has changed a bit and i can't find the safe get() and set() methods. get() seems to fail for Y values above 16 and the set() method seems to run into concurrent modification issues.