Paster Plugin Makes Giant Holes; Requires Rejoin [SOLVED]

Discussion in 'Plugin Development' started by Steeveeo, Jan 27, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Steeveeo

    Hello again, all. I have another question for you all.

    I'm working on a save/paste plugin that I like to call DupeBox (will probably be releasing that later). It pretty much just saves out cuboid sections of a world to a file, then loads that file in later and pastes it in a new location. It all works fine in the long run, performance-wise as well.

    My problem is that, while pasting, it tends to leave giant, client-side holes in the creation where blocks do not update to the clients until they rejoin. Sometimes, this even applies to the entire paste, making it invisible to everyone until they rejoin. Note, the players CAN actually "collide" with the paste (as in, they get bounced back by the server), but the blocks themselves are not updated.

    I've tried several different methods in updating the chunks to the clients: having the applyPhysics boolean in Block.setTypeIdAndData() set to true, running over the entire creation post-paste and enabling all the physics, and even using World.refreshChunk() on the affected chunks with varying degrees of failure. The first didn't do anything, the second only updated a couple of the nearby chunks to the player, and the last one outright crashed the server with a "TickNextTick out of synch" error until I added in a Thread.sleep(500) in between each chunk, in which it did the same as the first option.

    I MUST be missing something here, anyone care to help?

    Relevant code in spoiler:
    code (open)

    Code:java
    1.  
    2. //Load and paste blocks from serial data
    3. //NOTE: This might get a tad expensive if you have a lot of
    4. // plugins watching block placement or large pastes!
    5. // Slightly mitigated by the thing being Runnable and
    6. // having sleep()s here and there.
    7. for(int x = 0; x < xLength; x++)
    8. {
    9. for(int y = 0; y < yLength; y++)
    10. {
    11. for(int z = 0; z < zLength; z++)
    12. {
    13. double blockData = file.readDouble();
    14. int typeID = (int) Math.floor(blockData);
    15. byte data = (byte) Math.round((blockData - typeID) * 100);
    16.  
    17. //Get position for this block
    18. Location blockLoc = new Location(pasteCenter.getWorld(),pasteCenter.getX(),pasteCenter.getY(),pasteCenter.getZ());
    19. blockLoc.setX(blockLoc.getX() + x);
    20. blockLoc.setY(blockLoc.getY() + y);
    21. blockLoc.setZ(blockLoc.getZ() + z);
    22.  
    23. //If this block depends on another block to not break, add
    24. //to the populator list
    25. if(hookBlocks.contains(Material.getMaterial(typeID)))
    26. {
    27. //Extra sign data
    28. if(typeID == 63 || typeID == 68)
    29. {
    30. String dataString = file.readString();
    31. DupeBoxPopulatorBlock pop = new DupeBoxPopulatorBlock(blockLoc,typeID,data,dataString);
    32. hookBlockPopulator.add(pop);
    33. }
    34. else
    35. {
    36. DupeBoxPopulatorBlock pop = new DupeBoxPopulatorBlock(blockLoc,typeID,data);
    37. hookBlockPopulator.add(pop);
    38. }
    39. }
    40. else
    41. {
    42. pasteBlock(blockLoc, ply, typeID, data);
    43. }
    44. }
    45. }
    46. }
    47. [...]
    48.  
    49. //Populate all "hookblocks" now that every other block is here
    50. for(DupeBoxPopulatorBlock block : hookBlockPopulator)
    51. {
    52. pasteBlock(block.blockLoc, ply, block.type, block.data);
    53.  
    54. //Sign text data
    55. if(block.type == 63 || block.type == 68)
    56. {
    57. try
    58. {
    59. try{Thread.sleep(10);} catch (InterruptedException e){} //lolformatting
    60. String[] signData = util.explode(block.extraData, '\n');
    61. Sign sign = (Sign) block.blockLoc.getBlock().getState();
    62. sign.setLine(0, signData[0]);
    63. sign.setLine(1, signData[1]);
    64. sign.setLine(2, signData[2]);
    65. sign.setLine(3, signData[3]);
    66. }
    67. {
    68. System.out.println("[DupeBox] - WARNING: Could not get sign for text, moving on!");
    69. }
    70. }
    71. }
    72.  
    73. //Activate everything to properly update
    74. Location pasteEnd = new Location(pasteCenter.getWorld(), pasteCenter.getX(), pasteCenter.getY(), pasteCenter.getZ());
    75. activateRange(pasteCenter, pasteEnd);
    76. ply.sendMessage(ChatColor.GREEN + "[DupeBox] - Paste complete!");
    77.  


    Code:java
    1.  
    2. //Helper function to place blocks and do all
    3. //the hook checks and whatever else
    4. private static void pasteBlock(Location blockLoc, Player ply, int typeId, byte data)
    5. {
    6. //Setup place event and collect data
    7. Block block = blockLoc.getBlock();
    8. BlockState state = block.getState();
    9. block.setTypeId(typeId, false);
    10. block.setData(data, false);
    11. Block placeAgainst = block.getRelative(0, 0, 0);
    12. ItemStack inHand = new ItemStack(Material.AIR);
    13.  
    14. //Call and check event
    15. BlockPlaceEvent event = new BlockPlaceEvent(block, state, placeAgainst, inHand, ply, true);
    16. Bukkit.getServer().getPluginManager().callEvent(event);
    17. if(event.isCancelled())
    18. {
    19. block.setTypeIdAndData(state.getTypeId(), state.getRawData(), false);
    20. }
    21. }
    22.  


    Code:java
    1.  
    2. //Refresh all the chunks within a range in order to send to clients
    3. private static void activateRange(Location start, Location end)
    4. {
    5. LinkedList<Chunk> chunkList = new LinkedList<Chunk>();
    6. World world = start.getWorld();
    7.  
    8. //Get all chunks in here
    9. for(int x = (int) start.getX(); x < end.getX(); x++)
    10. {
    11. for(int z = (int) start.getZ(); z < end.getZ(); z++)
    12. {
    13. Chunk thisChunk = new Location(world,x,start.getY(),z).getChunk();
    14. if(!chunkList.contains(thisChunk))
    15. {
    16. chunkList.add(thisChunk);
    17. }
    18. }
    19. }
    20.  
    21. //Refresh chunks
    22. for(Chunk chunk : chunkList)
    23. {
    24. world.refreshChunk(chunk.getX(), chunk.getZ());
    25. try{Thread.sleep(500);} catch (InterruptedException e){} //lolformatting
    26. }
    27. }
    28.  

     
  2. Offline

    nisovin

    Are you running this inside of an asynchronous thread? If so, then that's probably your issue. Running block changes outside of the main server thread is a major no-no. If not, then your Thread.sleep() calls are just freezing up the server's processing for a few moments, but aren't really helping the server to keep up.
     
  3. Offline

    Steeveeo

    Well, it's either I run it in a thread, or have everyone drop from the server on large pastes.
     
  4. Offline

    nisovin

    Or you run it in a sync task and do the paste in chunks. You really can't do this with an async thread, you'll run into all sorts of problems.
     
  5. Offline

    Steeveeo

    Well, off to rewrite the paster for the third time, then. Well, at least I learned a little about Java threads this time through.
     
  6. Offline

    darkmage0252

    Maybe just resend the chunk's?
     
  7. Offline

    Steeveeo

    I mentioned trying that in the OP, World.refreshChunk() doesn't do much of anything under the current system. Although I guess I could do all the refreshing in a DelayedSyncTask if I want to do it the lazy way (if it works, of course).
     
  8. Offline

    Aetherspawn

    Chunks with <= 9 changes to them wont pass a chunk refresh. Make sure you manually flag the entire region for a refresh.
     
  9. Offline

    Steeveeo

    Mind specifying how?
     
  10. Offline

    bergerkiller

    Steeveeo see this

    If you want to alter chunk data:
     
    Steeveeo likes this.
  11. Offline

    Steeveeo

    Thumbs up for the good read there, bergerkiller. I will be toying with this for a bit today.
     
  12. Offline

    Steeveeo

    Might as well update on my progress, just in case someone else comes across this thread in search. Thanks to bergerkiller my DupeBox pasting is now freaking BLAZING fast, compared to what it was. However, I had to separate out the light-emitting blocks like torches and glowstone from the normal block geometry and paste them AFTER recalculating the chunk lighting using the Bukkit block type set functions, as chunk.initLighting() doesn't seem to add the light from those blocks when called manually (not sure how they generate properly in that case).

    I have a fort I work on regularly that I call Fort Sonora, and I've been using it as a base for testing this plugin, and it's about 70x40x80 in dimension. In version 0.25 of DupeBox, it took about a minute and a half to paste everything, now it takes on the order of 10 seconds, and I tend to like performance increases like this factor of 9 improvement!
     
    Derthmonuter and desht like this.
Thread Status:
Not open for further replies.

Share This Page