public String toString() {
   Camera camera = worldRenderer.getActiveCamera();
   if (targetBlockPos != null) {
     return String.format(
         "From: %f %f %f, Dir: %f %f %f, Hit %d %d %d %f %f %f",
         camera.getPosition().x,
         camera.getPosition().y,
         camera.getPosition().z,
         camera.getViewingDirection().x,
         camera.getViewingDirection().y,
         camera.getViewingDirection().z,
         targetBlockPos.x,
         targetBlockPos.y,
         targetBlockPos.z,
         hitPosition.x,
         hitPosition.y,
         hitPosition.z);
   }
   return "";
 }
  public void update(float delta) {
    // Repair lost target
    // TODO: Improvements to temporary chunk handling will remove the need for this
    boolean lostTarget = false;

    updateTarget();
    if (!target.exists()) {
      targetBlockPos = null;
      lostTarget = true;
    }

    // TODO: This will change when camera are handled better (via a component)
    Camera camera = worldRenderer.getActiveCamera();

    HitResult hitInfo =
        physics.rayTrace(
            new Vector3f(camera.getPosition()),
            new Vector3f(camera.getViewingDirection()),
            targetDistance,
            filter);
    updateFocalDistance(hitInfo, delta);
    Vector3i newBlockPos = null;

    EntityRef newTarget = EntityRef.NULL;
    if (hitInfo.isHit()) {
      newTarget = hitInfo.getEntity();
      hitPosition = hitInfo.getHitPoint();
      hitNormal = hitInfo.getHitNormal();
      if (hitInfo.isWorldHit()) {
        newBlockPos = new Vector3i(hitInfo.getBlockPosition());
      }
    }
    if (!Objects.equal(target, newTarget) || lostTarget) {
      EntityRef oldTarget = target;
      oldTarget.send(new CameraOutEvent());
      newTarget.send(new CameraOverEvent());
      localPlayer.getCharacterEntity().send(new CameraTargetChangedEvent(oldTarget, newTarget));
    }
    target = newTarget;
    targetBlockPos = newBlockPos;
  }