@Override
  public CharacterStateEvent step(
      CharacterStateEvent initial, CharacterMoveInputEvent input, EntityRef entity) {
    CharacterMovementComponent characterMovementComponent =
        entity.getComponent(CharacterMovementComponent.class);
    CharacterStateEvent result = new CharacterStateEvent(initial);
    result.setSequenceNumber(input.getSequenceNumber());
    if (worldProvider.isBlockRelevant(initial.getPosition())) {
      updatePosition(characterMovementComponent, result, input, entity);

      if (input.isFirstRun()) {
        checkBlockEntry(
            entity,
            new Vector3i(initial.getPosition(), 0.5f),
            new Vector3i(result.getPosition(), 0.5f),
            characterMovementComponent.height);
      }

      if (result.getMode() != MovementMode.GHOSTING && result.getMode() != MovementMode.NONE) {
        checkMode(characterMovementComponent, result, initial, entity, input.isFirstRun());
      }
    }
    result.setTime(initial.getTime() + input.getDeltaMs());
    updateRotation(characterMovementComponent, result, input);
    result.setPitch(input.getPitch());
    result.setYaw(input.getYaw());
    input.runComplete();
    return result;
  }
  private void climb(
      final CharacterStateEvent state, CharacterMoveInputEvent input, Vector3f desiredVelocity) {
    if (state.getClimbDirection() == null) {
      return;
    }
    Vector3f tmp;

    Vector3i climbDir3i = state.getClimbDirection();
    Vector3f climbDir3f = climbDir3i.toVector3f();

    Quat4f rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), 0, 0);
    tmp = new Vector3f(0.0f, 0.0f, -1.0f);
    QuaternionUtil.quatRotate(rotation, tmp, tmp);
    float angleToClimbDirection = tmp.angle(climbDir3f);

    boolean clearMovementToDirection = !state.isGrounded();

    // facing the ladder or looking down or up
    if (angleToClimbDirection < Math.PI / 4.0 || Math.abs(input.getPitch()) > 60f) {
      float pitchAmount = state.isGrounded() ? 45f : 90f;
      float pitch = input.getPitch() > 30f ? pitchAmount : -pitchAmount;
      rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), TeraMath.DEG_TO_RAD * pitch, 0);
      QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity);

      // looking sidewards from ladder
    } else if (angleToClimbDirection < Math.PI * 3.0 / 4.0) {
      float rollAmount = state.isGrounded() ? 45f : 90f;
      tmp = new Vector3f();
      QuaternionUtil.quatRotate(rotation, climbDir3f, tmp);
      float leftOrRight = tmp.x;
      float plusOrMinus = (leftOrRight < 0f ? -1.0f : 1.0f) * (climbDir3i.x != 0 ? -1.0f : 1.0f);
      rotation =
          new Quat4f(
              TeraMath.DEG_TO_RAD * input.getYaw(),
              0f,
              TeraMath.DEG_TO_RAD * rollAmount * plusOrMinus);
      QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity);

      // facing away from ladder
    } else {
      rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), 0, 0);
      QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity);
      clearMovementToDirection = false;
    }

    // clear out movement towards or away from the ladder
    if (clearMovementToDirection) {
      if (climbDir3i.x != 0) {
        desiredVelocity.x = 0f;
      }
      if (climbDir3i.z != 0) {
        desiredVelocity.z = 0f;
      }
    }
  }
 @SuppressWarnings(value = "SuspiciousNameCombination")
 private void updateRotation(
     CharacterMovementComponent movementComp,
     CharacterStateEvent result,
     CharacterMoveInputEvent input) {
   if (movementComp.faceMovementDirection && result.getVelocity().lengthSquared() > 0.01f) {
     float yaw = (float) Math.atan2(result.getVelocity().x, result.getVelocity().z);
     result.getRotation().set(new Vector3f(0, 1, 0), yaw);
   } else {
     result.getRotation().set(new Quat4f(TeraMath.DEG_TO_RAD * input.getYaw(), 0, 0));
   }
 }