Jump to content

Ipsissimus418

Members
  • Posts

    21
  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Ipsissimus418's Achievements

Tree Puncher

Tree Puncher (2/8)

0

Reputation

  1. That is something I hadn't considered in terms of threads. I have the WorldTickEvent handler do nothing on the client but run on the server. I may have some paths for the addEntry running on both the client and server. Something I need to look into and check if that is where the conflict is coming from. Thanks.
  2. I have a low occurrence issue reported by users of my mod which is resulting in a ConcurrentModificationException. I'm currently trying to investigate some possible options, since I'm having problems duplicating the problem and my understanding of a couple of Minecraft areas is not good. I've had a look at some of the Minecraft code, but I'm not understanding it well enough to satisfy my theories. I have a WorldTickEvent handler in my mod, that simply walks a list of block positions using a iter.hasNext loop. Each loop calls a simple piece of code on the current blockpos and then uses the iter.remove() to remove it from the list. .....onWorldTick(TickEvent.WorldTickEvent event) ..... .... MultiBlockTracker.run() [MultiBlockTracker.java] public void run() { .... Iterator<BlockPos> iter = blocks.iterator(); while (iter.hasNext()) { BlockPos pos = iter.next(); TileEntity te = world.getTileEntity(pos); if (te instanceof MultiBlockGlueProvider) { ((MultiBlockGlueProvider)te).getGlue().onHello(world, i.getPos()); iter.remove(); } } Each relevant tile entity in my mod can add blocks to this block list via an override of the TileEntity.validate() method. [MultiBlockTileEntity.java] @Override public void validate() { super.validate(); if (!world.isRemote) { MultiBlockTracker.get().addEntry(pos); } } [MultiBlockTracker.java] public void addEntry(BlockPos pos) { blocks.add(new BlockPos(pos)); } The ConcurrentModificationException is pointing at the iter.next. java.util.ConcurrentModificationException: null at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) ~[?:1.8.0_181] {} at java.util.ArrayList$Itr.next(ArrayList.java:859) ~[?:1.8.0_181] {} at ipsis.woot.modules.factory.multiblock.MultiBlockTracker.run(MultiBlockTracker.java:45) ~[woot:1.16.4-1.0.3.0] {re:classloading} As far as I can see I am using the iterator correctly and have used the same pattern successfully(?) elsewhere in my code. My current guess is that the concurrent modification is because one thread is trying to add to this list, while the other one is trying to remove items from it. However I'm not sure if this is how Minecraft actually works, because I'm not confident in my understanding of the world tick. So does anyone know what threads of execution are actually going on here Can the world tick event and the tile entity validate be running in different threads causing this concurrent access to happen and I need to add some locking around the block list? Or is there a different thread for each world meaning I could have two WorldTickEvent at the same time therefore I've got two iterators running over the same list removing items at the "same time"? Or am I just looking at a red herring and the problem actually lies elsewhere? Thanks
  3. After a good day of debugging I have my answer. Server was working perfectly. It was the client that was failing. When the client was processing the Container.slotClick it does the following slot6.getItemStackLimit SlotItemHandler.getItemStackLimit Get a full stack and try to insert it into the IItemHandler calls isValidItemForSlot which on the client was returning false. My isItemValidForSlot looks up the recipes loaded on startup to see if the item is a valid input. When running with an internal server, this input item list was being created by my code by the server part. However on a standalone client, it was always empty as the server code isn't there to fill it. Therefore the client updated the gui with a an empty itemstack as the item wasn't valid. My transferStackInSlot also used this input item validation code, which is why it didn't work. By removing this input slot validation just now, then it all works. So back to adding some code to make the validation work on both sides. I hadn't appreciated that the item validation was being performed by client code, but that seems to be because the client is refreshing the stacks to show the user in the gui. The server only corrects those stack if the client shows something that is out of sync with its knowledge. Well that is my understanding of what is going on, so I go and fix my code and be more appreciative of the differences between the client and server operation.
  4. Been doing some more debugging - including a completely fresh Intellj checkout. Items being inserted via a hopper while the gui is open work correctly - probably because they modify the destination stack size. You can correctly remove (manually or shift-click) an item from the machine inventory. The issue is limited to items going into the single slot of the machine either via mouse drag or via shift-click on the item. I've rewritten the container slot handling and the tlle entity inventory using other people's code as an example but nothing seems to fix it. It looks very much like when the item is dragged into the machine slot via the gui, then the slot update packet is not being sent to the client. The update only happens when something else causes the machine inventory stack to change eg. item processed, that the client is refreshed. And even when that happens only the destination slot is refreshed, not the source slot or the dragged stack. (And by that I mean that I've broken it in some way).
  5. (I initially asked for help of Discord where Gigaherz referenced his transferStackInSlot method which I've tried, however I'm still having issue, so I'm posting here with more context) I have a few machines with inventory slots and have implemented the whole transferStackInSlot with both my own and then Gigaherz version. With an internal server moving items manually from the inventory to the input slot of the machine or via transferStackInSlot works fine. GUI updates immediately and there are no visual issues. I've retested with a dedicated server and client and now things dont work so well. There are a few issues * delay between moving the item and the gui refreshing * original itemstack remains attached to the cursor or with shift-click visually moves to another slot in the inventory eg. manual version Drag an item stack of 10 items into the machine slot Machine starts processing A few seconds later the destination slot renders the stack (handleSetSlot breakpoint not hit for about 4 seconds on the client) Stack is still visually in the dragged state and I can place it in the inventory Double clicking this fake stack makes it vanish. eg. Shift-click version Shift click item from toolbar to machine Machine starts processing, destination slot is empty A few seconds later the destination slot renders the stack (handleSetSlot breakpoint not hit for about 4 seconds on the client) Stack visually moves to the player inventory Double clicking this fake stack makes it vanish. The 4 second delay to the handleSetSlot seems to line up with the time for the machine to process one item and therefore decrement the input stack size. I would almost say that the slot numbering on the dedicated server and client are different. There are no issues with a standalone client and internal server. Container code : https://github.com/Ipsis/Woot/blob/1.15.x/src/main/java/ipsis/woot/modules/squeezer/blocks/DyeSqueezerContainer.java TileEntity code : https://github.com/Ipsis/Woot/blob/1.15.x/src/main/java/ipsis/woot/modules/squeezer/blocks/DyeSqueezerTileEntity.java I know I've overlooked something but I completely stumped as to what. I had other issues related to dedicated server which I've fixed when registering of the containers. So any pointers as to what might be going on here. Thanks Ipsis
  6. Cannot believe it was that simple. Shifting the setSlimeSize to after the onInitialSpawn fixed the issue and now the slimes spawn with the correct sizes. Now to go back and remove all the debug. Thanks diesieben07.
  7. I'm currently updating/rewriting/experimenting with my mod for 1.14.4 and have been running into a confusing issue with spawning. I can happily spawn mobs and kill them, except for when I need to modify the entity. The best example is that I need to spawn and kill small slimes. I have an access transformer for setSlimeSize that is working fine. I then use setSlimeSize to set a size of 1 - small. But when I get the LivingDropEvent ~50% of the time the size is back to 2 - large. I've been dumping the UUID for the entity at creation time and drop event time, to make sure I'm actually seeing the same entity and they look fine. To create the entity I've been basing it off the AbstractSpawner.java vanilla code, then I set the slime size public net.minecraft.entity.monster.SlimeEntity func_70799_a(IZ)V # setSlimeSize Entity entity = EntityType.func_220335_a(..... .... MobEntity mobEntity = (MobEntity)entity; .... SlimeEntity slimeEntity = (SlimeEntity)mobEntity; if (fakeMob.isSmallSlime()) { slimeEntity.setSlimeSize(1, false); } else { slimeEntity.setSlimeSize(2, false); } ... mobEntity.onInitialSpawn(world, world.getDifficultyForLocation(new BlockPos(entity)), SpawnReason.SPAWNER, null, null); ... mobEntity.onDeath(DamageSource.causePlayerDamage(fakePlayer)); I don't know if I'm missing some extra step after calling setSlimeSize to sync the change before it gets spawned. Looking at SlimeEntity,java I'm not seeing anything jump out. Any suggestions as to what I could be missing out here? eg The "set size to small 1" debug is from " LOGGER.debug("SlimeSpawner: set size to small {}", slimeEntity.getSlimeSize());" Good spawn 18:34:48] [Server thread/INFO] [ip.wo.Woot/]: Spawning and killing minecraft:slime,small@1 ipsis.woot.simulation.dimension.TartarusDimension@a4cf04a [18:34:48] [Server thread/DEBUG] [ip.wo.Woot/]: SlimeSpawner: set size to small 1 [18:34:48] [Server thread/DEBUG] [ip.wo.Woot/]: SpawnController:spawnKill minecraft:slime,small@1 1858da71-6e5c-48ce-9615-3bbbf501abb6 [18:34:48] [Server thread/INFO] [ip.wo.Woot/]: onLivingDropsEvent fake kill minecraft:slime,small@1 1858da71-6e5c-48ce-9615-3bbbf501abb6 [ItemEntity['Slimeball'/324, l='Single Factory', x=4.00, y=20.00, z=4.00]] Bad spawn [18:34:48] [Server thread/INFO] [ip.wo.Woot/]: Spawning and killing minecraft:slime,small@3 ipsis.woot.simulation.dimension.TartarusDimension@a4cf04a [18:34:48] [Server thread/DEBUG] [ip.wo.Woot/]: SlimeSpawner: set size to small 1 [18:34:48] [Server thread/DEBUG] [ip.wo.Woot/]: SpawnController:spawnKill minecraft:slime,small@3 b5a5a679-6972-48b9-b93d-2826c37801c5 [18:34:48] [Server thread/INFO] [ip.wo.Woot/]: onLivingDropsEvent fake kill minecraft:slime,large@3 b5a5a679-6972-48b9-b93d-2826c37801c5 [] [18:34:48] [Server thread/ERROR] [ip.wo.Woot/]: onLivingDropEvent: Large slime found SlimeEntity['Slime'/320, l='Single Factory', x=4.00, y=52.00, z=4.00] FakePlayer['[woot_3]'/125, l='Single Factory', x=0.50, y=5.00, z=-47.50] size 2
  8. @diesieben07 Well that is a embarrassing mistake! Thanks for that, I looked straight through that code multiple times and never saw the mistake. Correcting the canInteractWith method allowed the GUI to open successfully.
  9. Mine is a bit different. I'm successfully calling NetworkHooks.openGui on the server side and the request is getting to the client side handler as well.
  10. I've updated my code to use the new gui opening code, however whenever I click on the block, the GUI never displays. (Forge 1.13.2-25.0.73) https://github.com/Ipsis/Woot/tree/1.13.x So far I've done the following * registered an extension point for returning the guiscreen ** https://github.com/Ipsis/Woot/blob/1.13.x/src/main/java/ipsis/woot/Woot.java#L68 * return a new GuiScreen for the FMLPlayMessages,OpenContainer ** https://github.com/Ipsis/Woot/blob/1.13.x/src/main/java/ipsis/woot/client/GuiHandler.java#L30 * used NetworkHooks.openGui in the onBlockActivated method for the block ** https://github.com/Ipsis/Woot/blob/1.13.x/src/main/java/ipsis/woot/util/WootBlock.java#L92 If I walk through the code then I can see the client side code being called, the correct block position being passed, the correct gui id being requested and the GuiScreen object being returned. Then FMLPlayMessages.handle method calls the displayGuiScreen method successfully, no obvious errors seem to occur and no event seems to be cancelled. That particular method gets to the end without issue. I've put a breakpoint in the GUI drawGuiContainerBackgroundLayer and that is never hit. Adding debug to the FMLPlayMessage.OpenContainer method handler in my code shows that I can call the gui open sequence repeatedly by right-clicking the block. I'm assuming that I'm missing something really simple but I cannot for the life of me work out what it is. Does anyone have any hints as to what obvious thing I'm overlooking. Thanks. Ipsis
  11. I've started to cache the last few entity.getCachedUniqueIdString and then used that to compare incoming event, then dropping the event if I've already seen that id. This seems to filter out the multiple Dragon ones without having any obvious impact on other entity deaths.
  12. That actually fits in with a piece of code in EntityDragon attackDragonFrom if health <= 0.0F && !currentphase.isStationary setHealth(1.0F) setPhase(DYING) That seems to tick the Dragon back up by 1 health if you attack it while it is dying, but still moving. It would therefore be allowed to reach the onDeath multiple times as the health > 0.0F. If that is the case, I might try caching the last EntityID in my event handler to try and filter out the duplicate Dragon ones. It wont be 100% successful, but it should cut them down a bit.
  13. In my mod I use the onLivingDeath event to decide if a user has killed enough of a specific mob for something to occur. I recently had an issue raised on GitHub where the user said that they could keep hitting the Dragon with arrows while it was dying and they could register multiple deaths. I've added some debug to my event handler and sure enough, if I was quick enough with a bow I could get multiple events raised until a point in the EnderDragon death sequence eg. public class HandlerLivingDeathEvent { @SubscribeEvent public void onLivingDeathEvent(LivingDeathEvent event) { LogHelper.info("onLivingDeathEvent: " + event.getSource() + "/" + event.getEntity() + "/" + event.getEntity().isDead); new Exception().printStackTrace(); Gave the output [12:26:23] [Server thread/INFO]: Woot: onLivingDeathEvent: net.minecraft.util.EntityDamageSourceIndirect@70bea580/EntityDragon['Ender Dragon'/277010, l='Mob Test', x=7.20, y=84.56, z=-28.32]/false [12:26:23] [main/INFO]: Woot: onLivingDeathEvent: net.minecraft.util.DamageSource@369d50fa/EntityDragon['Ender Dragon'/277010, l='MpServer', x=7.50, y=84.50, z=-29.54]/false [12:26:24] [Server thread/INFO]: Woot: onLivingDeathEvent: net.minecraft.util.EntityDamageSourceIndirect@764bce76/EntityDragon['Ender Dragon'/277010, l='Mob Test', x=7.99, y=80.26, z=-24.57]/false [12:26:25] [Server thread/INFO]: Woot: onLivingDeathEvent: net.minecraft.util.EntityDamageSourceIndirect@4a8c7eaa/EntityDragon['Ender Dragon'/277010, l='Mob Test', x=9.94, y=68.92, z=-15.77]/false So I managed three server events for onLivingDeath and one client event. On another run I managed to get six server events and one client event. I'm only summoning one EnderDragon so it can only be for the one mob. Dumping the stack for the server event showed the same path for each event [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at ipsis.woot.event.HandlerLivingDeathEvent.onLivingDeathEvent(HandlerLivingDeathEvent.java:21) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_188_HandlerLivingDeathEvent_onLivingDeathEvent_LivingDeathEvent.invoke(.dynamic) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:90) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:179) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraftforge.common.ForgeHooks.onLivingDeath(ForgeHooks.java:600) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.EntityLivingBase.onDeath(EntityLivingBase.java:1282) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.EntityLivingBase.attackEntityFrom(EntityLivingBase.java:1127) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.boss.EntityDragon.attackDragonFrom(EntityDragon.java:643) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.boss.EntityDragon.attackEntityFromPart(EntityDragon.java:601) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.MultiPartEntityPart.attackEntityFrom(MultiPartEntityPart.java:51) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.projectile.EntityArrow.onHit(EntityArrow.java:391) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.projectile.EntityArrow.onUpdate(EntityArrow.java:286) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.entity.projectile.EntityTippedArrow.onUpdate(EntityTippedArrow.java:110) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.world.World.updateEntityWithOptionalForce(World.java:2168) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.world.WorldServer.updateEntityWithOptionalForce(WorldServer.java:871) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.world.World.updateEntity(World.java:2127) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.world.World.updateEntities(World.java:1928) [12:29:54] [Server thread/INFO]: [ipsis.woot.event.HandlerLivingDeathEvent:onLivingDeathEvent:21]: at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:643) This is using Forge 1.12.2-14.23.3.2655. I have a few mods in my dev environment for testing, but nothing is jumping out as having any impact on the Dragon death sequence. Looking at the methods in the stack trace I'm not seeing any obvious path that allows this. attackEntityFromPart(dragonPart, source, damage) damage = modify damage if neede attackDragonFrom(source, damage) attackEntityFrom(source, damage) raise onLivingAttack event if health <= 0.0F return damageEntity if health <= 0.0F onDeath raise onLivingDeath event So am I just using the wrong event to register the death occurring in my code, or is this a known "quirk" of the Dragon death and the fact that it is a multi-part mob?
  14. So TileEntity::validate did the trick. First try it got called on both the server and the client - which created a very impressive stack overflow. Limiting that to the server - which is where I needed to do it - allowed me to walk to the master. One initial concern was the algorithm I use to walk to the master was travelling over other TEs in the same chunk which had not yet had their validate methods called yet. However as I only use their position to determine the path and never try to access them, I don't think that is an issue. I'm now horribly embarrassed for investigating every other method in block and TE and completely overlooking the "validate" one. Thanks diesieben07
  15. I currently have a working multiblock with a mixture of ticking and non-ticking TEs. The main block (heart/master) is a ticking TE with a public method interruptStructure which basically tells it to do a scan of the multiblock. All the other blocks are non-ticking TEs which when block.onBlockAdded is called, find the nearest reachable heart and call master.interruptStructure. They do something similar for when the block/te is invalidated. So I've got a way to say hello-block.onBlockAdded and goodbye-te.invalidate This seems to work nicely until someone builds the multiblock over a chunk boundary The problems occurs when the player moves away from the multiblock and returns. When the first chunk loads and it contains the heart, then the heart does an initial scan and fails since some of the multiblock is in the unloaded chunk. The player moves further forward, the next chunk loads and then the rest of the multiblock is loaded, but cannot say hello because there is no block.onBlockAdded for a chunk load. My solution - which I don't like - is to make them all tickable, then on the first tick of the non-heart blocks, find the master and poke it. All subsequent ticks for that TE are then no-ops. But of course I've just registered possibly 60 new ticking TEs per multiblock - which doesn't seem right. So does anyone have any suggestions of a way to fix this that doesn't involve making them all ITickable? Possible alternative: Keep track of which chunks have heart TEs and then when a neighbouring chunk loads, use that list to force the heart to rescan. Thanks Ipsis
×
×
  • Create New...

Important Information

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