Resource Previewing and pasting schematics block-by-block

Discussion in 'Spigot Plugin Development' started by SamB440, Jun 20, 2018.

  1. Hello,

    Today I will be showing you a cool way of letting a player build a schematic, for whatever you want to do. I've made this thread because I searched for this myself and it was hard to get to this point, so I thought somebody else might look for the same thing :)

    Please note that this uses deprecated methods, so there might be a better way to do this. There may also be things I can do better. If so, please let me know! I have also used lombok so you may need to change some things.

    What I will be showing you today is something like this: https://gfycat.com/AfraidGentleJapanesebeetle

    Create this class first (I use it to easily cancel schedulers): https://gitlab.com/SamB440/Schematics-Extended/blob/master/Scheduler.java

    Please read through the code to understand what each part does.

    Pasting a schematic block by block only
    To paste a schematic block by block only, you will need this class here: gitlab.com.

    Now you will want to paste it. You can do this using:
    Code (Java):
    Schematic schematic = new Schematic(plugin, file to schematic);

    schematic.pasteSchematic(location origin, paster);
    Optionally you can create an outline of the schematic that is being pasted, using the locations returned in a list from the #pasteSchematic method:
    Code (Java):
    Scheduler scheduler = new Scheduler();
                     
                            scheduler.setTask(Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
                                for(Location loc : locations)
                                {
                                    if(loc.getBlock().getType() == Material.AIR) loc.getWorld().spawnParticle(Particle.VILLAGER_HAPPY, loc.getX() + 0.5, loc.getY(), loc.getZ() + 0.5, 2);
                                }
                         
                                if(locations.get(locations.size() - 1).getBlock().getType() != Material.AIR || schematic.isPasted())
                                {
                                    scheduler.cancel();
                                    schematic.setPasted(false);
                                }
                            }, 0, 40));
    Pasting a schematic block by block with a preview
    Before we begin, you will first need a way to tell whether the player is building, and what they are building. If you don't have an easy way to do this currently, I made a handy PlayerManagement class.

    Inside your main class, create the method #getPlayerManagement. Inside your onEnable:
    Code (Java):
    management = new PlayerManagement(this);
    Method:
    Code (Java):
    public void getPlayerManagement()
    {
       return management;
    }
    Create a leave and join listener. Inside your leave and join listener, use:
    Code (Java):
    plugin.getPlayerManagement().setBuilding(player.getUniqueId(), false, "null");
    Once you've done this, you will need to use the full version of the Schematic class, which you can find here.

    Next create a move listener class, to keep track of where the player is and what direction they're looking, so we can update the preview accordingly. You could also use a repeating task, which would be less resource-intensive. I recommend using a scheduler.

    Warning: Make sure you check the player is building first using #isBuilding!

    If you're using a scheduler, use this class. I made it for your use. You will still need to change it to fit into your plugin, but the main part you need to worry about is the scheduler within.

    To display the schematic preview correctly, I used this code for the move listener (outdated, resource-intensive):
    Code (Java):
    List<Location> locations = plugin.getPlayerManagement().getBuilding(player.getUniqueId()).pasteSchematic(player.getTargetBlock(null, 7).getLocation().add(0, 1, 0), player, true);
                 
                        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
                            List<Location> locations2 = plugin.getPlayerManagement().getBuilding(player.getUniqueId()).pasteSchematic(player.getTargetBlock(null, 7).getLocation().add(0, 1, 0), player, true);
                            for(Location location : locations)
                            {
                                if(!locations2.contains(location)) location.getBlock().getState().update();
                            }
                        }, 20);

    The schematic preview Y will be fixed to the players Y so they can't paste it in the air, and they can paste it a maximum of 7 blocks away from their position. To remove the fake blocks that are no longer valid and created by the preview, we update the state of those blocks.

    Note this line of code here:
    Code (Java):
    plugin.getPlayerManagement().getBuilding(player.getUniqueId()).pasteSchematic(player.getTargetBlock(null, 7).getLocation().add(0, 1, 0), player, true);
    This gets the current building the player is pasting.

    Create a way for your player to paste a schematic. I made an interact listener that pasted the schematic on right click of air or a block.

    Code (Java):

    if(plugin.getPlayerManagement().isBuilding(player.getUniqueId()))
    {
                       Schematic schematic = plugin.getPlayerManagement().getBuilding(player.getUniqueId());
                       List<Location> locations = schematic.pasteSchematic(player.getTargetBlock(null, 7).getLocation().add(0, 1, 0), player, false);
                       if(locations != null)
                       {
                     
                           Scheduler scheduler = new Scheduler();
                     
                           scheduler.setTask(Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
                               for(Location loc : locations)
                               {
                                   if(loc.getBlock().getType() == Material.AIR) loc.getWorld().spawnParticle(Particle.VILLAGER_HAPPY, loc.getX() + 0.5, loc.getY(), loc.getZ() + 0.5, 2);
                               }
                         
                               if(locations.get(locations.size() - 1).getBlock().getType() != Material.AIR || schematic.isPasted())
                               {
                                   scheduler.cancel();
                                   schematic.setPasted(false);
                               }
                           }, 0, 40));
                           player.sendMessage(ChatColor.GREEN + "Forge is now being built!");
                           plugin.getPlayerManagement().setBuilding(player.getUniqueId(), false, "null");
                       } else {
                           player.sendMessage(ChatColor.RED + "You can't build that here!");
                       }
                   }
     
    Notice first how I changed the last value in this method from true to false:
    Code (Java):
                       List<Location> locations = schematic.pasteSchematic(player.getTargetBlock(null, 7).getLocation().add(0, 1, 0), player, false);
    False tells the schematic to begin pasting block-by-block instead of displaying a preview.

    The method #pasteSchematic will return null if any blocks are interfering with the schematic placement, so we add a null check to make sure it is in a valid area. Then we create an outline of the schematic using particles, and then set the player's building to false.

    Skip this if you don't use a scheduler
    In your main class, create a getter for your BuildTask. Then, add this section of code when the player places the schematic:
    Code (Java):
                            if(plugin.getBuildTask().getCache().containsKey(player))
                            {
                                for(Location clear : plugin.getBuildTask().getCache().get(player))
                                {
                                    clear.getBlock().getState().update(true, false);
                                }
                            }
    I hope I explained this well enough to you, and if you have any suggestions or questions blast away. This is my first resource so I probably got a lot of things wrong, but I thought this was pretty cool :p

    License
    You are free to use this in your plugin as you want, as long as you comply with the license, which can be found here: https://gitlab.com/SamB440/Schematics-Extended/blob/master/LICENSE.

    I also ask you keep the credits in the source, and add me as a contributor to your resource page and/or an author in plugin.yml.
     
    #1 SamB440, Jun 20, 2018
    Last edited: Sep 8, 2018
    • Like Like x 4
    • Winner Winner x 1
    • Creative Creative x 1
  2. It's fixed now, please try again!
     
  3. Update #1
    Schematics are now centered instead of being at a corner. Should allow for easier placement.

    Uploaded latest code to gitlab.
     
  4. Why corner or center? There is a location in schematics for the base/start point. It's usually the location the player was standing when they created the schematic.

    Also, can you provide a video of your examples in action? I'm most curious about this preview you talk about. Been thinking of making a server side equivalent of the client mod "schematica" and am curious if it's similar to my idea (if so, I might use this)
     
  5. I'm afraid not, there's a gif in the post but a friend made it for me. And it sounds like it is - you can read the outline of a schematic and use player#sendBlockChange to send fake blocks to a player.
    Hmm, this is what I thought too, but according to the schematic format wiki this doesn't exist! Could you direct me on what this is in the schematic file?

    EDIT: Could it possibly be this?
    • WEOriginX: WorldEdit-only origin X coordinate; optional but if one is provided all parts should be.

    • WEOriginY: WorldEdit-only origin Y coordinate; optional but if one is provided all parts should be.

    • WEOriginZ: WorldEdit-only origin Z coordinate; optional but if one is provided all parts should be.
     
    #5 SamB440, Jun 21, 2018
    Last edited: Jun 21, 2018
  6. Update #2
    • Fixed Y using paster's location instead of location provided. This was actually a feature, not a bug. My bad lol.
    • Extended the block "blacklist" - blocks that will be placed last. This should reduce the amount of blocks that will be broken due to being placed at the incorrect time. Grass still seems to be a problem, though!
    • Renamed block by block only version to "SchematicReplacer.java" to distinguish between the two if you want both in your project.
    Sorry for updating twice a day :p

    Elsewhere, I did some testing today by placing 7 schematics at once with 225 blocks each to test lag. TPS stayed at ~20. Yay! Kinda need to do this in a live environment to simulate this properly though...

    Something cool you can do, is banner colours on schematic creation:
    [​IMG]
    [​IMG]

    Uploaded latest code to gitlab.

    EDIT:

    Update #3

    • Blocks will no longer be affected by physics
    • Changed code in main post to be less resource-intensive (now uses a scheduler and has been further optimised)
    • Using the new code should remove any fake blocks left behind, making everything feel nicer
    Uploaded latest code to gitlab.
     
    #6 SamB440, Jun 21, 2018
    Last edited: Jun 21, 2018
    • Like Like x 1
  7. Update #4
    • Improved buildtask by:
      • Making some parts of updating async
      • Better detection of where fake blocks currently are
    • Water is now detected as an invalid placement location
    Small update that was mostly performance increases and making everything look better. Enjoy :)!

    If you have any suggestions, please let me know! I'm looking to constantly improve this.

    Uploaded latest code to gitlab.
     
  8. Good resource!. but the page said that I don't have permission to view it?. Why?
     
  9. Uh? You should do. I changed the permissions so you can. Please show me a screenshot.

    EDIT: yep works for me whilst signed out, maybe gitlab has banned you or they don't allow VPNs or something

    [​IMG]
     
  10. Uhh I'm not using any vpn. no perms.png

    Here's the ss
     
  11. I believe he is talking about this link because it also doesn't work for me.
     
  12. Thanks, changed it and it works now!
     
    • Like Like x 1
  13. Update #5
    It's been a while. This is a small update and tbh I'm running out of ideas lol.
    • Added optional time
      • e.g putting 10 would place 2 blocks every second.
    This is the last update, unless anyone has some ideas. Enjoy!
     
  14. I have an idea. It's like building rotation. If player is looking at the west, building will build to the west and etc.
     
  15. I already tried to do this, but it seems to be quite difficult... I just couldn't get it to work. You'd also have the issue of stairs.
     
  16. Is your resource working on 1.13? They removed type IDs and Data...
     
  17. The resource cannot be updated until WorldEdit changes the way it saves schematics.
     
  18. Update #6
    Following on from this post, I have now updated this to 1.13.1!

    Several bugs have been fixed, too. I highly recommend you update as soon as possible! Links are in the OP.

    Thanks to @brainsynder for the help!
     
    • Useful Useful x 1

Share This Page