Jump to content

jabelar

Members
  • Posts

    3266
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by jabelar

  1. The general idea is correct. However there are a couple Java things plus a couple Minecraft things that probably need to be fixed. Java Stuff: First of all, you have a private field called timer but you're incrementing something called tickCounter. Actually I'm not sure why Eclipse isn't warning about the fact that tickCounter isn't even declared, and also I'm not sure why it even would run for you at all. I think everywhere you have tickCounter you should replace with timer. Minecraft Stuff: How did you register the class as an event handler? Just annotating the method isn't enough. You need to register the whole class to the event bus. Also, as mentioned you only want the event to fire on the client. So you should use the client tick event. Since you reference the Minecraft class, you should also technically make sure that bit of code is client side only. There are a couple approaches to this: Put the code with Minecraft into a method that runs through your proxy system. The concept of the proxy is very important in Minecraft modding. Basically it means you create a method with same name/parameters that has different code on client versus server. In this case, your client-side version would have the code you used, and the server side would just do nothing. Alternatively you could make your whole event handling class client-side only (by annotating it as sided) and only register it on the client side. For the registration you could that again using the proxy, or you could switch to the annotation style of event handler registration and indicate that it is sided in the annotation. If you haven't modded before, all the points about client versus server might be confusing, but it is worthwhile taking time to fully understand it as it is critical to bug-free modding.
  2. The fact that arrows work but player hitting doesn't supports my idea that the problem is somewhere in the mouse over method. Once the arrows are flying they use their own collision box to check for collisions. When the player tries to hit something it checks the space around the "look vector" which involves methods like the one I've mentioned. So I still feel the problem can be understood by looking at the EntityRenderer.getMouseOver() method. I'm not certain that radius field is the problem, but it sure sounded like it should be related. Maybe you should use debug mode in Eclipse and set a breakpoint for when user clicks button and step through to see what is happening.
  3. I think it should work. I've done giants and dinosaurs before. The vanilla giant zombie is 6 times regular zombie size and the main part of a vanilla dragon is 16 by 8. There might be a limit though. Maybe start small and increase it a bit at a time to see if there is an actual limit. I looked through the code a bit. The key bit of code is the EntityRenderer.getMouseOver() method. This is what decides if the player is clicking on an entity. One thing I noticed is that one thing it does it it narrows down the entities to check for using the World.getEntitiesWithinAABBExcludingEntity() method. I looked at that method and it actually has a comment that there is a MAX_ENTITY_RADIUS field that: * Used in the getEntitiesWithinAABB functions to expand the search area for entities. * Modders should change this variable to a higher value if it is less then the radius * of one of there entities. This field is a public static field so is accessible for you. So I think what you need to do is at some time during loading you need to set that field to the size of your entity. The default is 2. So it makes sense that you might need to change this in your case. Anyway, try that and see if it works.
  4. Approach 1) You can basically do what doors and fences and beds do. Basically they sort of figure out what their neighbors are and the orientation and render accordingly. Approach 2) If you want to allow the player to place it all at once, then you can just check when block placed (using event) and place all the blocks for the structure accordingly. Similarly, you can handle the breaking block event and break them all. That is if you really want it to be a single thing that gets placed and/or destroyed all at once.
  5. I think the NameFormat event might work more specifically. Pretty sure it is called on both sides, as it fires every time EntityPlayer.getDisplayName() is called.
  6. Maybe you can handle an event like living hurt, death or an attack event and check if damage was caused by a sword. Whichever ievent s most close to the explosion. Then prevent it from happening by canceling the vanilla behavior and replacing with your own.
  7. You shouldn't register the class to the event bus if you're already using the annotation and static method. Alternatively you should not make method static. I think your event handler registration isn't right.
  8. It shouldn't be that hard to go through. If you're using Eclipse just go to the World class and find the source to the playEvent() method. Then just highlight the method name and right-click and select Call Hierarchy. That will show you all the places the method is called from. I just did that and there is only about 30 places, so would only take about 10 minutes to just go down the calls and see all the ints. In fact for "fun" I just did it: playDispenseSound 1000 (default), 1001 (fail), 1002 (projectile), 1004 (fireworks), or 1018 (fire charge) ItemEnderEye.onItemRightClick 1003 BlockDoor.onNeighborChanged and blockActivated and toggleDoor1005 (iron door opening), 1006 (non-iron doors opening), 1011 (iron door closing), 1012 (non-iron door closing) BlookFenceGate.onNeighborChanged and blockActivated1008 (if powered), 1014 (not powered) World.extinguishFire 1009 ItemRecord.onItemUse and dropRecord 1010 AIFireBallAttack.updateTask 1015, 1016, 1018 (as the attack progresses dragon.phase.PhaseStrafePlayer 1017 EntityAIBreakDoor 1019 (randomly about once per second, 1021 if door breaks (in Hard mode). EntityWither.updateAITasks 1022 (something to do with destroying blocks?) EntityWither 1023 (explosion occurred) launchWitherSkull 1024 EntityBat.updateAITasks 1025 (when leaves hanging position) EntityZombie.onKillEntity 1026 EntityZombieVillager.finishConversion 1027 EntityDragon.onDeathUpdate 1028 BlockAnvil.onBroken 1029 ContainerRepair.onTake 1029 or 1030 depending on anvil repair result BlockAnvil.onEndFalling 1031 EntityPlayerMP.changeDimension 1032 BlockChorusFlower.placeGrownFlower 1033 BlockChorusFlower.placeDeadFlower 1034 brewPotions 1035 BlockTrapDoor.playSound 1036 (iron door open), 1037 (iron door close), 1007 (non-iron door open), 1013 (non-iron door close) ItemEnderEye 1038 spawnDispenseParticles 2000 Various methods that destroy blocks 2001 EntityXPBottle.onImpact 2002 EntityEnderEye.onUpdate 2003 updateSpawner 2004 dispenceStack also ItemDye.onItemUse 2005 EntityDragonFireball.onImpact 2006 EntityPotion.onImpact 2007 if instant effect, 2002 otherwise DragonFightManager generateGateway 3000 DragonSpawnManager.process 3001 I think that should be about all of them. I did it pretty fast so you might want to double check any that you're actually planning to use.
  9. Yes, that is the general idea. There is actually a PlayerTickEvent which is a bit more specific, which I prefer because it is a bit wasteful to call your method for every living entity, but since you test immediately for player it probably isn't a bit deal. I don't think your methods for checking the block and setting the block are quite right. It looks like you're trying to directly cast the position into the block state. Instead you should use the player world (player.world) and use the getBlockState() method. Also generally shouldn't compare to the default state. It would work for grass because I think it doesn't have any properties, but I'd probably do it like this: if (player.onGround && player.world.getBlockState(playerUPos).getBlock() == Blocks.GRASS)) and to set the block something like: player.world.setBlockState(Blocks.STONE.getDefaultState())
  10. You should usually post what you've tried so we can tell what kind of help you need. Basically, you can use a player tick event handler and check if the player is on the ground (there is a public field called onGround) and if that is true, then just check the block in the position under the player position to confirm it is grass or whatever and then set that block to stone or whatever.
  11. Also, you can simply look at the Type Hierarchy (in Eclipse or Structure in IDEA) of the Minecraft class and it will list all the available fields and methods. Even if it is a long list it doesn't take much time to scroll through and look for things that look correct. In that you'll see that the player field is public and of type EntityPlayerSP. If you then look at the Type Hierarchy for EntityPlayerSP, I see that i have a public method called sendChatMessage() that takes a string parameter. So definitely player (not thePlayer which doesn't exist) is the field you want. And for me, in 1.12.2 it has the right method available.
  12. Did you look at the code for BlockFence? Did you look at what they do for collision bounding box?
  13. It has nothing to do with randomness and more about how specific each step has to be. In most cases in the Minecraft world there are several paths between two points, but in your case there is exactly one correct next step. Then if you think about the general problem of finding the best path when there is only one exact path that works you can see how the problem gets extremely hard. Think about actually trying to code it. What should the first step be? You might think that stepping in the direction of the target is best, but in general case that isn't guaranteed. Maybe you should go sideways a bit, or backwards. Your example only goes upwards but what about a case where you have to go down a bit and then back up? No matter how you code your pathfinding you can always build a case that is bad for that algorithm. In the end the only way to guarantee finding a path is by brute force, meaning try every possible valid move. But that is still hard. In your example, if you spawned the entity right under the target block the correct block requires about 30 exact steps to get to. So you would need an algorithm that finds every combination of 30 steps to ensure you find the correct one. Sometimes it will be found early and sometimes at the end. With smarter algorithms you can make sure you don't include trying paths that cover ground you've already been on. But it still adds up. If you just do the first level of math, in each step you have 4 directions you can go. So the number of actual paths is 4 raised to the power of 30! That is 1.15x10^18 paths -- or 1.15 million million million! You can cut that down by assuming you don't go back to the space you just came from, but that is still 3^30. Anyway, the number of possible paths is extremely large. The only way to really make it manageable is to limit the number of steps that the pathfinding can look for and hope that getting closer to the target is helpful overall (not always true, but often is). The interesting thing is this shows how good humans are at pathfinding. We can look at a picture and almost instantly understand the path, probably because our brain can take advantage of some massively parallel processing that isn't available on a PC running Minecraft. Again, there is a lot of research into this -- it is important for robotics and gaming and is interesting from a general math and computer science theory. But even the best algorithms are pretty limited. EDIT: If you really want the entity to find the path using vanilla pathfinding I think it should be possible by adding more "waypoints" -- basically intermediate targets within the range of the pathfinding.
  14. They're usually not "bugs" in the pathfinding but rather performance optimization. If you study pathfinding you see that making a pathfinding that can handle every case can quickly cause performance problems. You can easily create tricky paths (like the one in your picture, or things like large mazes) where the pathfinding will have to give up before it solves it otherwise would cause lag. Think about it -- a path finding algorithm may have to try every possible combination of path within the 3D space and that will grow quickly. It is possible to implement your own pathfinding. For example, one time I was trying an entity that could destroy blocks in its way so I had to figure out some custom pathfinding that allowed it to find paths through those blocks. Pathfinding is extremely well documented in both math and computer science so you can google to find algorithms that help you. But eventually the sheer number of possible paths in a large 3D space will limit your ability to explore them all.
  15. As mentioned the MInecraft#currentServerData probably gives you enough info for what you need. It has a boolean to indicate lan and also contains the IP address of the server. I'm thinking (you should test this) that the IP address would always be the loopback local address (127.0.0.1) in single player mode. So if the isOnLan() method returns true, you know you're on LAN. Otherwise check the serverIP field to see if it is 127.0.0.1 (in which case you'd assume single player) and otherwise assume multiplayer.
  16. Out of curiosity, why would you want this client-side? Client-side only mods are usually intended to add some visual interest or other changes to user input, but I can't really imagine why a person would want to restrict themselves from being able to pick up something client side if the server otherwise is allowing other players to do so.
  17. I think it works like this. For your capability since on server you need NBT to save / load, you should have the Capability.IStorage methods for readNBT() and writeNBT() implemented which can be accessed through convenience methods Capability#readNBT() and Capability#writeNBT(). And on server you can get the Capability with the ItemStack#getCapability() method. Lastly, the Item class has the getShareTag() and getNBTShareTag() methods to allow you to add NBT data to the packets that are used to sync to client. Putting it together, I think you want to do the following: Make sure your custom Capability IStorage properly implements a readNBT() and writeNBT() method. In your custom Item class make sure that the getShareTag() returns true and that the getNBTShareTag() method adds the cability NBT (which you can get from Capability#writeNBT()). IMPORTANT: You will want to specify some sort of key that you can use on client side to extract the data. In your client rendering or wherever, you use look in the ItemStack NBT for the key that you specified in Step 2 above. You should not need to actually get that NBT info into the client Capability, I think you can just use it directly from the NBT. Note that to make it simpler you don't have to transmit the whole Capability NBT, if you just want a simple piece of data you could just transmit that instead. In any case, the key is the getNBTShareTag() and you just have to look up the data from the capability and come up with (there is some flexibility here) a NBT element to add that you can inspect on the client side. One other thing, it isn't clear to me whether changes in Capability will trigger an ItemStack update packet or not. There have been various PRs over the years discussing this. My point is you may want to force the update by sending the vanilla packet when your Capability changes. Lastly, I find that working with NBT is technically simple but easy to get confused in the details because the compounds can get complex due to the fact they can be nested -- you can have a list of compound, a compound with lists, and so forth. So I highly recommend using console print statements throughout your code to print out the tag information at critical points so you can trace what is going on.
  18. On the client you can open a GUI any time you want with the Minecraft.getMinecraft().displayGuiScreen() method. Of course that needs to be called from code that is running on the client because the Minecraft class is client-side only. So I usually put that in a proxy method. Like in your IProxy interface you can add a method called openMyGui() and in the client proxy implementation run the code above but of course pass in your custom GUI instance to the displayGuiScreen() method, and in your server proxy implementation you can do nothing. Then you can just call proxy.openMyGui() whenever you want to open your GUI. In your case you said you want to do it in response to a key bind, so in your code that runs when the keybind is activated just put the proxy.openMyGui() call there.
  19. When saying you can't figure something out, it is good to explain what you've tried and where you got stuck. For example, you know that the 3rd person view does similar thing so what have you learned about how vanilla 3rd person view works? Which classes are involved? What is the difficulty in recreating the same thing for your mod?
  20. Your pastbin links aren't working so I can't see your code. But you should make sure you have the following method in your block class: @Override @SideOnly(Side.CLIENT) public BlockRenderLayer getBlockLayer() { return BlockRenderLayer.TRANSLUCENT; }
  21. At some point anything that makes major functionality is invasive. Like if I handle an event and change the health of a player and another mod does too, there isn't any way to prevent that. The best thing is to find specific conflicts if they arrive and work with the other mod (or use understanding of their source if available) to resolve it. Also, the recipe book you're replacing with is a copy of the vanilla and extends it, so any code that calls any recipe book function will still work. So if they are calling add() or remove() or such they will work exactly the same. The custom recipe book extends the RecipeBookServer class so can be passed as a parameter and tested for type just like the vanilla one. By the way, one other thing to think about. The approach I give above allows you to prevent the unlocking. However, I am not changing when the unlocking is called. In other words, if I pick up a stick and don't have the player condition, but then later get the player condition I would have to pick up the stick again to unlock it. That might be what you want, but if you want to create new situations where there is a chance to unlock you can simply call the EntityPlayerMP.unlockRecipes() method at any time. Like if you want all dye recipes to unlock after player does something, you can just call the unlockRecipes() method. You still haven't mentioned what player conditions you want to control the unlocking, so don't know what you need. Basically, I've given you an approach to prevent vanilla unlocking, and the way to force vanilla unlocking already exists.
  22. Ha! At least it was your old stuff..
  23. Okay, yeah, I think my last idea works. It seems pretty easy although maybe needs more testing. Basically the idea is to replace the recipeBook instance field with an extended custom recipe book class that checks a player condition before adding a recipe. The recipeBook field is instantiated for EntityPlayerMP in several situations so I basically just did a player tick event handler that checks to see if the recipe book is already replaced or not. So in a properly registered/annotated event handling class I put: public static Field recipeBook = ReflectionHelper.findField(EntityPlayerMP.class, "recipeBook", "field_192036_cb"); @SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true) public static void onEvent(PlayerTickEvent event) { if (event.player instanceof EntityPlayerMP) { EntityPlayerMP playerMP = (EntityPlayerMP) event.player; RecipeBookServer recipeBookCurrent = playerMP.getRecipeBook(); if (!(recipeBookCurrent instanceof RecipeBookServerCustom)) { // DEBUG System.out.println("Replacing recipe book with custom book"); RecipeBookServerCustom recipeBookNew = new RecipeBookServerCustom(); recipeBookNew.copyFrom(recipeBookCurrent); try { recipeBook.set(playerMP, recipeBookNew); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } } } And my custom recipe class is pretty simple: public class RecipeBookServerCustom extends RecipeBookServer { @SuppressWarnings("deprecation") @Override public void add(List<IRecipe> recipesIn, EntityPlayerMP player) { List<IRecipe> list = Lists.<IRecipe>newArrayList(); for (IRecipe irecipe : recipesIn) { if (!recipes.get(getRecipeId(irecipe)) && !irecipe.isDynamic()) { if (!player.world.isDaytime()) // <--- Put whatever player condition here { // DEBUG System.out.println("Unlocking recipe"); unlock(irecipe); markNew(irecipe); list.add(irecipe); CriteriaTriggers.RECIPE_UNLOCKED.trigger(player, irecipe); net.minecraftforge.common.ForgeHooks.sendRecipeBook(player.connection, SPacketRecipeBook.State.ADD, recipesIn, Collections.emptyList(), isGuiOpen, isFilteringCraftable); } else { // DEBUG System.out.println("Can't unlock recipe as player condition not met"); } } else { net.minecraftforge.common.ForgeHooks.sendRecipeBook(player.connection, SPacketRecipeBook.State.ADD, recipesIn, Collections.emptyList(), isGuiOpen, isFilteringCraftable); } } } } For testing, my player condition was simple -- I just checked if the player world was currently in nighttime or daytime and only allowed unlocking recipes during nighttime. You would of course need to change that to any player condition you want -- like they need to be wearing something, or have some capability, or whatever. As far as my quick testing it seemed to work. You can see I added console statements to help testing. So in the daytime if you pick up any new ingredients nothing happens and the console will indicate that recipe wasn't unlocked, but in the daytime if you pick up new ingredients it adds recipe normally. Probably needs a lot more testing, but it seems like it should generally work. Now, one problem is that you didn't really specify what sort of player conditions you were interested in. My solution above is about *unlocking recipes" based on the player condition. But once they are unlocked they are unlocked and even if the condition changes they won't get locked again. What exactly is the behavior you wanted?
  24. Normally an event would be perfect for that. You could just cancel it or return a DENY result. However, unfortunately the ItemCraftedEvent doesn't seem to do that. Like you can certainly null out the result, but I think the ingredients would still be consumed and it would take some work to undo that. Is this for your own recipes or for vanilla recipes. For your own recipes I think you can implement your own IRecipe that check the player conditions in the matches() method. I'm thinking maybe another way to approach this is to try to replace the vanilla advancements for unlocking recipes. But that would be a lot of work and would require reflection and a lot of JSONs. I just thought of another possible approach that just might work. The CriteriaTriggers.RECIPE_UNLOCKED field could be modified (might need reflection cause it is final, but it is already public) to use your own trigger instead where extend the vanilla one by override the trigger() and maybe the test() methods. EDIT: There may be even more places to intercept things. Not sure which is the easiest. But for example, the EntityPlayerMP recipe book instance itself could be replaced with a custom version where you override the add() method. That might be the most logical place to intercept actually because that is where the recipe is actually unlocked and also where the advancement trigger is fired.
  25. Well, you should give it a shot. First of all, diesieben07 was linking to it in 2017 and it is his own tutorial so he probably knows what is relevant or not. Also, even when things change between versions the concepts are usually sound -- like maybe name of a method changes or something but the general idea of getting the player from the container is likely still valid. However if you look at the tutorial, it also starts with a hint about another possible approach. There is the PlayerEvent.ItemCraftedEvent which you can handle. I might suggest starting with that. You really didn't say much about what sort of conditional behavior you want. Like if you want to just deny crafting recipes to players, then it might be very simple. If you want to use entirely different recipes that might get more complicated. For example, the ItemCraftedEvent will pass you the whole crafting matrix, but it would be up to you to try to match it against alternate recipes. But it is all possible.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.