Shynixn / StructureBlockLib

StructureBlockLib is a bukkit implementation for handling structures on spigot server.
https://shynixn.github.io/StructureBlockLib/apidocs/
MIT License
60 stars 5 forks source link

Structure entities not disappearing after removal #101

Closed plytki closed 2 years ago

plytki commented 2 years ago

I'm handling the entities and I'm adding them to list and I'm trying to remove them later. I store entities in a list. image But when I try to remove them they are not removing. image

What's wrong with that?

Shynixn commented 2 years ago

GetEntity does not return the added entity. Instead it is a representation of the stored nbt data using the Entity interface. The actual entities are not accessible at this stage. Just query the chunk/world before and after loading a structure to get the added delta of entities.

plytki commented 2 years ago

I don't want to risk like that. Can't I just get the uuids of entities and then get them or add NBT data to the entity and then based on that identify them when getting entities from chunk? I want it to be stable.

Shynixn commented 2 years ago

The NMS implementation from mojang removes the UUID tag (and other not known tags) before spawning. There isn't really anything I can do except my previous suggestion because unfortunatly the entity spawning is quite closed.

Another "more stable" suggestion would be to disable spawning entities, collecting them in a list and spawning them yourself after the structure has been placed.

  List<StructureEntity<Entity, Location>> structureEntities = new ArrayList<>();
  List<Entity> entities = new ArrayList<>(); // Final list of the spawned entities you can fully control
  Location targetLocation = ..;
  structureBlockLibApi
          .loadStructure(plugin)
          .at(targetLocation)
          .includeEntities(false)
          .onProcessEntity(structureEntity -> {
              structureEntities.add(structureEntity);
              return false;
          })
          .loadFromPath(..)
          .onResult(e -> {
              for(StructureEntity<Entity, Location> entity : structureEntities){
                  Location entityLocation =  // I am not sure about this calculation. You need to check it yourself.
                          targetLocation.clone().add(entity.getSourceLocation());

                  entity.spawnEntity(entityLocation).ifPresent(addedEntity -> {
                      entities.add(addedEntity);
                  });               
              }
          });
plytki commented 2 years ago

The NMS implementation from mojang removes the UUID tag (and other not known tags) before spawning. There isn't really anything I can do except my previous suggestion because unfortunatly the entity spawning is quite closed.

Another "more stable" suggestion would be to disable spawning entities, collecting them in a list and spawning them yourself after the structure has been placed.

  List<StructureEntity<Entity, Location>> structureEntities = new ArrayList<>();
  List<Entity> entities = new ArrayList<>(); // Final list of the spawned entities you can fully control
  Location targetLocation = ..;
  structureBlockLibApi
          .loadStructure(plugin)
          .at(targetLocation)
          .includeEntities(false)
          .onProcessEntity(structureEntity -> {
              structureEntities.add(structureEntity);
              return false;
          })
          .loadFromPath(..)
          .onResult(e -> {
              for(StructureEntity<Entity, Location> entity : structureEntities){
                  Location entityLocation =  // I am not sure about this calculation. You need to check it yourself.
                          targetLocation.clone().add(entity.getSourceLocation());

                  entity.spawnEntity(entityLocation).ifPresent(addedEntity -> {
                      entities.add(addedEntity);
                  });               
              }
          });

Does not work for me. I can't get entities to spawn.

image

image

Shynixn commented 2 years ago

There might be a exception swallowed by the completeable futures. Put a try catch around the code in onResult

plytki commented 2 years ago

image

I got this error, but when I try converting and adding as a vector I get this.

image

The proper one is on the left side but the pasted one is the right one.

image

plytki commented 2 years ago

I think I need the rotation processing with my list of entities.

Shynixn commented 2 years ago

The structureEntity.getEntity may contain the rotation data somewhere. (Maybe in getlocation, getYaw?). It should just be a wrapper around the nbt data.

plytki commented 2 years ago

I don't really know how the API works so It's going to be pain in the ass to solve that issue.

plytki commented 2 years ago

Also when I will base on my calculations I won't have access to rotation API system which is very important for me.

Shynixn commented 2 years ago
for (StructureEntity<Entity, Location> entity : entities) {
                                entity.getEntity().ifPresent(virtualEntity -> {
                                    Location entityLocation =  // I am not sure about this calculation. You need to check it yourself.
                                    targetLocation.clone().add(entity.getSourceLocation().toVector());
                                   Location l = virtualEntity.getLocation();
                                    entityLocation.setYaw(l.getYaw());
                                    entityLocation.setPitch(l.getPitch());
                                    entity.spawnEntity( entityLocation);
                                });
                            }

Did you try it like that?

plytki commented 2 years ago

image Yes

plytki commented 2 years ago

The only way I got it working is this but It's not the nicest way to do that. "/ If there is going be other way to do that please inform me.

      System.out.println("trying to place furniture");
      this.placing = true;
      Set<Entity> beforePasting = new HashSet<>();
      HashMap<Long, Chunk> chunks = new HashMap<>();
      StructureBlockLibApi.INSTANCE
              .loadStructure(plugin)
              .at(targetLocation)
              .includeEntities(true)
              .onProcessEntity(e -> {
                  e.getEntity().ifPresent(entity -> {
                      Location entityLocation = targetLocation.clone().add(e.getSourceLocation().toVector());
                      Chunk chunk = entityLocation.getChunk();
                      if (!chunks.containsKey(chunk.getChunkKey())) {
                          chunks.put(chunk.getChunkKey(), chunk);
                      }
                      Chunk chunk2 = entity.getChunk();
                      if (!chunks.containsKey(chunk2.getChunkKey())) {
                          chunks.put(chunk2.getChunkKey(), chunk2);
                      }
                  });
                  return false;
              })
              .onProcessBlock(e -> false)
              .rotation(rotation)
              .loadFromPath(nbtFile.toPath())
              .onException(e -> plugin.getLogger().log(Level.SEVERE, "Failed to load structure.", e))
              .onResult(e -> {
                  this.usedChunks = new HashMap<>(chunks);
                  chunks.forEach((aLong, chunk) -> {
                      beforePasting.addAll(Arrays.asList(chunk.getEntities()));
                  });
                  StructureBlockLibApi.INSTANCE
                          .loadStructure(plugin)
                          .at(targetLocation)
                          .includeEntities(true)
                          .onProcessEntity(process -> true)
                          .onProcessBlock(process -> false)
                          .rotation(rotation)
                          .loadFromPath(nbtFile.toPath())
                          .onException(process -> plugin.getLogger().log(Level.SEVERE, "Failed to load structure.", process))
                          .onResult(process -> {
                              new BukkitRunnable() {
                                  @Override
                                  public void run() {
                                      Set<Entity> allEntities = new HashSet<>();
                                      usedChunks.forEach((aLong, chunk) -> {
                                          System.out.println("checking chunk: " + aLong);
                                          for (Entity entity : chunk.getEntities()) {
                                              if (!(entity instanceof Player)) {
                                                  allEntities.add(entity);
                                              }
                                          }
                                      });
                                      plugin.getLogger().log(Level.INFO, ChatColor.GREEN + "Loaded structure '" + nbtFile.getName() + "'.");
                                      allEntities.removeAll(beforePasting);
                                      entities = allEntities;
                                      System.out.println(entities.size());
                                      placing = false;
                                  }
                              }.runTaskLater(PartiEffects.getInstance(), 5);
                          });
              });
Shynixn commented 2 years ago

Nice that you got it to work. This behaviour wasn't really the intention of the API (just basic loading/saving structures), so I guess this is the way to do it.

plytki commented 2 years ago

I'll paste my final code here in case anyone would need it too.

Set<Entity> beforePasting = new HashSet<>();
        StructureBlockLibApi.INSTANCE
                .loadStructure(plugin)
                .at(targetLocation)
                .includeEntities(true)
                .onProcessEntity(e -> {
                    e.getEntity().ifPresent(entity -> {
                        Location entityLocation = targetLocation.clone().add(e.getSourceLocation().toVector());
                        Chunk chunk = entityLocation.getChunk();
                        if (!usedChunks.containsKey(chunk.getChunkKey())) {
                            usedChunks.put(chunk.getChunkKey(), chunk);
                        }
                    });
                    return false;
                })
                .onProcessBlock(e -> false)
                .rotation(rotation)
                .loadFromPath(nbtFile.toPath())
                .onException(e -> plugin.getLogger().log(Level.SEVERE, "Failed to load structure.", e))
                .onResult(e -> {
                    usedChunks.forEach((aLong, chunk) -> {
                        beforePasting.addAll(Arrays.asList(chunk.getEntities()));
                    });
                    StructureBlockLibApi.INSTANCE
                            .loadStructure(plugin)
                            .at(targetLocation)
                            .includeEntities(true)
                            .onProcessEntity(process -> true)
                            .onProcessBlock(process -> false)
                            .rotation(rotation)
                            .loadFromPath(nbtFile.toPath())
                            .onException(process -> plugin.getLogger().log(Level.SEVERE, "Failed to load structure.", process))
                            .onResult(process -> {
                                Set<Entity> allEntities = new HashSet<>();
                                usedChunks.forEach((aLong, chunk) -> {
                                    //System.out.println("checking chunk: " + aLong);
                                    for (Entity entity : chunk.getEntities()) {
                                        if (!(entity instanceof Player)) {
                                            allEntities.add(entity);
                                        }
                                    }
                                });
                                allEntities.removeAll(beforePasting);
                                entities = allEntities;
                                for (Entity entity : entities) {
                                    assignData(entity);
                                }
                                //System.out.println(entities.size());
                                placing = false;
                                plugin.getLogger().log(Level.INFO, "Loaded structure '" + nbtFile.getName() + "'.");
                            });
                });

Also you have to block from placing other entities in the chunks that are affected by the structurelib during pasting the structure.