public EntityRef newInstance(BlockFamily blockFamily, int quantity) {
   if (blockFamily == null) {
     return EntityRef.NULL;
   }
   EntityBuilder builder = newBuilder(blockFamily, quantity);
   return builder.build();
 }
  /**
   * Transforms a block entity with the change of block type. This is driven from the delta between
   * the old and new block type prefabs, but takes into account changes made to the block entity.
   *
   * @param blockEntity The entity to update
   * @param oldType The previous type of the block
   * @param type The new type of the block
   */
  private void updateBlockEntityComponents(
      EntityRef blockEntity,
      Block oldType,
      Block type,
      Set<Class<? extends Component>> retainComponents) {
    BlockComponent blockComponent = blockEntity.getComponent(BlockComponent.class);

    Optional<Prefab> oldPrefab = oldType.getPrefab();
    EntityBuilder oldEntityBuilder = entityManager.newBuilder(oldPrefab.orElse(null));
    oldEntityBuilder.addComponent(
        new BlockComponent(oldType, new Vector3i(blockComponent.getPosition())));
    BeforeEntityCreated oldEntityEvent =
        new BeforeEntityCreated(oldPrefab.orElse(null), oldEntityBuilder.iterateComponents());
    blockEntity.send(oldEntityEvent);
    for (Component comp : oldEntityEvent.getResultComponents()) {
      oldEntityBuilder.addComponent(comp);
    }

    Optional<Prefab> newPrefab = type.getPrefab();
    EntityBuilder newEntityBuilder = entityManager.newBuilder(newPrefab.orElse(null));
    newEntityBuilder.addComponent(
        new BlockComponent(type, new Vector3i(blockComponent.getPosition())));
    BeforeEntityCreated newEntityEvent =
        new BeforeEntityCreated(newPrefab.orElse(null), newEntityBuilder.iterateComponents());
    blockEntity.send(newEntityEvent);
    for (Component comp : newEntityEvent.getResultComponents()) {
      newEntityBuilder.addComponent(comp);
    }

    for (Component component : blockEntity.iterateComponents()) {
      if (!COMMON_BLOCK_COMPONENTS.contains(component.getClass())
          && !entityManager
              .getComponentLibrary()
              .getMetadata(component.getClass())
              .isRetainUnalteredOnBlockChange()
          && !newEntityBuilder.hasComponent(component.getClass())
          && !retainComponents.contains(component.getClass())) {
        blockEntity.removeComponent(component.getClass());
      }
    }

    blockComponent.setBlock(type);
    blockEntity.saveComponent(blockComponent);

    HealthComponent health = blockEntity.getComponent(HealthComponent.class);
    if (health == null && type.isDestructible()) {
      blockEntity.addComponent(
          new HealthComponent(type.getHardness(), type.getHardness() / BLOCK_REGEN_SECONDS, 1.0f));
    } else if (health != null && !type.isDestructible()) {
      blockEntity.removeComponent(HealthComponent.class);
    } else if (health != null && type.isDestructible()) {
      health.maxHealth = type.getHardness();
      health.currentHealth = Math.min(health.currentHealth, health.maxHealth);
      blockEntity.saveComponent(health);
    }

    for (Component comp : newEntityBuilder.iterateComponents()) {
      copyIntoPrefab(blockEntity, comp, retainComponents);
    }
  }
  private EntityRef createBlockEntity(Vector3i blockPosition, Block block) {
    EntityBuilder builder = entityManager.newBuilder(block.getPrefab().orElse(null));
    builder.addComponent(new LocationComponent(blockPosition.toVector3f()));
    builder.addComponent(new BlockComponent(block, blockPosition));
    if (block.isDestructible() && !builder.hasComponent(HealthComponent.class)) {
      // Block regen should always take the same amount of time,  regardless of its hardness
      builder.addComponent(
          new HealthComponent(
              block.getHardness(), block.getHardness() / BLOCK_REGEN_SECONDS, 1.0f));
    }
    boolean isTemporary = isTemporaryBlock(builder, block);
    if (!isTemporary && !builder.hasComponent(NetworkComponent.class)) {
      builder.addComponent(new NetworkComponent());
    }

    EntityRef blockEntity;
    if (isTemporary) {
      blockEntity = builder.buildWithoutLifecycleEvents();
      temporaryBlockEntities.add(blockEntity);
    } else {
      blockEntity = builder.build();
    }

    blockEntityLookup.put(new Vector3i(blockPosition), blockEntity);
    return blockEntity;
  }
 public EntityRef deserialize(EntityData.PackedEntity entityData) {
   EntityBuilder target;
   if (entityData.hasParentPrefabUri()) {
     target = entityManager.newBuilder(entityData.getParentPrefabUri());
   } else {
     target = entityManager.newBuilder();
   }
   deserializeOnto(target, entityData);
   if (entityData.hasId()) {
     return entityManager.createEntityWithId(entityData.getId(), target.iterateComponents());
   } else {
     return target.build();
   }
 }
  public EntityRef newInstance(BlockFamily blockFamily, int quantity) {
    if (blockFamily == null) {
      return EntityRef.NULL;
    }

    EntityBuilder builder = entityManager.newBuilder("engine:blockItemBase");
    if (blockFamily.getArchetypeBlock().getLuminance() > 0) {
      builder.addComponent(new LightComponent());
    }

    // Copy the components from block prefab into the block item
    Prefab prefab = Assets.getPrefab(blockFamily.getArchetypeBlock().getPrefab());
    if (prefab != null) {
      for (Component component : prefab.iterateComponents()) {
        if (component.getClass().getAnnotation(AddToBlockBasedItem.class) != null) {
          builder.addComponent(entityManager.getComponentLibrary().copy(component));
        }
      }
    }

    DisplayNameComponent displayNameComponent = builder.getComponent(DisplayNameComponent.class);
    displayNameComponent.name = blockFamily.getDisplayName();

    ItemComponent item = builder.getComponent(ItemComponent.class);
    if (blockFamily.getArchetypeBlock().isStackable()) {
      item.stackId = "block:" + blockFamily.getURI().toString();
      item.stackCount = (byte) quantity;
    }

    BlockItemComponent blockItem = builder.getComponent(BlockItemComponent.class);
    blockItem.blockFamily = blockFamily;

    return builder.build();
  }
  @ReceiveEvent
  public void onActivate(
      ActivateEvent event, EntityRef entity, TunnelActionComponent tunnelActionComponent) {

    Vector3f dir = new Vector3f(event.getDirection());
    dir.scale(4.0f);
    Vector3f origin = new Vector3f(event.getOrigin());
    origin.add(dir);
    Vector3i blockPos = new Vector3i();

    int particleEffects = 0;
    int blockCounter = tunnelActionComponent.maxDestroyedBlocks;
    for (int s = 0; s <= tunnelActionComponent.maxTunnelDepth; s++) {
      origin.add(dir);
      if (!worldProvider.isBlockRelevant(origin)) {
        break;
      }

      for (int i = 0; i < tunnelActionComponent.maxRaysCast; i++) {
        Vector3f direction = random.nextVector3f();
        Vector3f impulse = new Vector3f(direction);
        impulse.scale(tunnelActionComponent.explosiveForce);

        for (int j = 0; j < 3; j++) {
          Vector3f target = new Vector3f(origin);

          target.x += direction.x * j;
          target.y += direction.y * j;
          target.z += direction.z * j;

          blockPos.set((int) target.x, (int) target.y, (int) target.z);

          Block currentBlock = worldProvider.getBlock(blockPos);

          if (currentBlock.isDestructible()) {
            if (particleEffects < tunnelActionComponent.maxParticalEffects) {
              EntityBuilder builder = entityManager.newBuilder("engine:smokeExplosion");
              builder.getComponent(LocationComponent.class).setWorldPosition(target);
              builder.build();
              particleEffects++;
            }
            if (random.nextFloat() < tunnelActionComponent.thoroughness) {
              EntityRef blockEntity = blockEntityRegistry.getEntityAt(blockPos);
              blockEntity.send(
                  new DoDamageEvent(
                      tunnelActionComponent.damageAmount, tunnelActionComponent.damageType));
            }

            blockCounter--;
          }

          if (blockCounter <= 0) {
            return;
          }
        }
      }
    }
    // No blocks were destroyed, so cancel the event
    if (blockCounter == tunnelActionComponent.maxDestroyedBlocks) {
      event.consume();
    }
  }