<commit_before>var Vec3 = require('vec3').Vec3
  , assert = require('assert')
  , math = require('../math')
  , conv = require('../conversions')

module.exports = inject;

var EPSILON = 0.000001
  , PI = Math.PI
  , PI_2 = Math.PI * 2
  , POSITION_UPDATE_INTERVAL_MS = 50
  , PHYSICS_INTERVAL_MS = 20

function inject(bot) {
  var physics = {
    maxGroundSpeed: 4.27, // according to the internet
    terminalVelocity: 20.0, // guess
    walkingAcceleration: 100.0, // seems good
    gravity: 27.0, // seems good
    groundFriction: 0.9, // seems good
    playerApothem: 0.32, // notch's client F3 says 0.30, but that caused spankings
    playerHeight: 1.74, // tested with a binary search
    jumpSpeed: 9.0, // seems good
    yawSpeed: 3.0, // seems good
    sprintSpeed: 1.3, // correct
  };

  var controlState = {
    forward: false,
    back: false,
    left: false,
    right: false,
    jump: false,
    sprint: false,
  };
  var jumpQueued = false;
  var lastSentYaw = null;
  var positionUpdateTimer = null;
  var doPhysicsTimer = null;
  var lastPositionSentTime = null;
  var lastPhyicsFrameTime = null;

  function doPhysics() {
    var now = new Date();
    var deltaSeconds = (now - lastPhyicsFrameTime) / 1000;
    lastPhyicsFrameTime = now;
    nextFrame(deltaSeconds);
  }

  function cleanup() {
    stopPhysics();
    stopPositionUpdates();
  }

  function stopPositionUpdates() {
    clearInterval(positionUpdateTimer);
    positionUpdateTimer = null;
  }

  function stopPhysics() {
    clearInterval(doPhysicsTimer);
    doPhysicsTimer = null;
  }

  function nextFrame(deltaSeconds) {
    if (deltaSeconds < EPSILON) return; // too fast
    var pos = bot.entity.position;
    var vel = bot.entity.velocity;

    // derive xy movement vector from controls
    var movementRight = 0;
    if (controlState.right) movementRight += 1;
    if (controlState.left) movementRight -= 1;
    var movementForward = 0;
    if (controlState.forward) movementForward += 1;
    if (controlState.back) movementForward -= 1;

    // acceleration is m/s/s
    var acceleration = new Vec3(0, 0, 0);
    if (movementForward || movementRight) {
      // input acceleration
      var rotationFromInput = Math.atan2(-movementRight, movementForward);
      var inputYaw = bot.entity.yaw + rotationFromInput;
      acceleration.x += physics.walkingAcceleration * -Math.sin(inputYaw);
      acceleration.z += physics.walkingAcceleration * -Math.cos(inputYaw);
      if (controlState.sprint) {
        acceleration.x *= physics.sprintSpeed;
        acceleration.z *= physics.sprintSpeed;
      }
    }

    // jumping
    if ((controlState.jump || jumpQueued) && bot.entity.onGround) {
      vel.y = physics.jumpSpeed;
    }
    jumpQueued = false;

    // gravity
    acceleration.y -= physics.gravity;

    var oldGroundSpeedSquared = calcGroundSpeedSquared();
    if (oldGroundSpeedSquared < EPSILON) {
      // stopped
      vel.x = 0;
      vel.z = 0;
    } else {
      // non-zero ground speed
      var oldGroundSpeed = Math.sqrt(oldGroundSpeedSquared);
      var groundFriction = physics.groundFriction * physics.walkingAcceleration;
      // less friction for air
      if (! bot.entity.onGround) groundFriction *= 0.05;
      // if friction would stop the motion, do it
      var maybeNewGroundFriction = oldGroundSpeed / deltaSeconds;
      groundFriction = groundFriction > maybeNewGroundFriction ?
        maybeNewGroundFriction : groundFriction;
      acceleration.x -= vel.x / oldGroundSpeed * groundFriction;
      acceleration.z -= vel.z / oldGroundSpeed * groundFriction;
    }

    // calculate new speed
    vel.add(acceleration.scaled(deltaSeconds))

    // limit speed
    var groundSpeedSquared = calcGroundSpeedSquared();
    if (groundSpeedSquared > physics.maxGroundSpeed * physics.maxGroundSpeed) {
      var groundSpeed = Math.sqrt(groundSpeedSquared);
      var correctionScale = physics.maxGroundSpeed / groundSpeed;
      vel.x *= correctionScale;
      vel.z *= correctionScale;
    }
    vel.y = math.clamp(-physics.terminalVelocity, vel.y, physics.terminalVelocity);

    // calculate new positions and resolve collisions
    var boundingBox = getBoundingBox();
    var boundingBoxMin, boundingBoxMax;
    if (vel.x !== 0) {
      pos.x += vel.x * deltaSeconds;
      var blockX = Math.floor(pos.x + math.sign(vel.x) * physics.playerApothem);
      boundingBoxMin = new Vec3(blockX, boundingBox.min.y, boundingBox.min.z);
      boundingBoxMax = new Vec3(blockX, boundingBox.max.y, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.x = blockX + (vel.x < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.x = 0;
        boundingBox = getBoundingBox();
      }
    }

    if (vel.z !== 0) {
      pos.z += vel.z * deltaSeconds;
      var blockZ = Math.floor(pos.z + math.sign(vel.z) * physics.playerApothem);
      boundingBoxMin = new Vec3(boundingBox.min.x, boundingBox.min.y, blockZ);
      boundingBoxMax = new Vec3(boundingBox.max.x, boundingBox.max.y, blockZ);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.z = blockZ + (vel.z < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.z = 0;
        boundingBox = getBoundingBox();
      }
    }


    bot.entity.onGround = false;
    if (vel.y !== 0) {
      pos.y += vel.y * deltaSeconds;
      var playerHalfHeight = physics.playerHeight / 2;
      var blockY = Math.floor(pos.y + playerHalfHeight + math.sign(vel.y) * playerHalfHeight);
      boundingBoxMin = new Vec3(boundingBox.min.x, blockY, boundingBox.min.z);
      boundingBoxMax = new Vec3(boundingBox.max.x, blockY, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.y = blockY + (vel.y < 0 ? 1 : -physics.playerHeight) * 1.001;
        bot.entity.onGround = vel.y < 0 ? true : bot.entity.onGround;
        vel.y = 0;
      }
    }

    bot.emit('move');
  }

  function collisionInRange(boundingBoxMin, boundingBoxMax) {
    var cursor = new Vec3(0, 0, 0);
    var block;
    for (cursor.x = boundingBoxMin.x; cursor.x <= boundingBoxMax.x; cursor.x++) {
      for (cursor.y = boundingBoxMin.y; cursor.y <= boundingBoxMax.y; cursor.y++) {
        for (cursor.z = boundingBoxMin.z; cursor.z <= boundingBoxMax.z; cursor.z++) {
          block = bot.blockAt(cursor);
          if (block && block.boundingBox === 'block') return true;
        }
      }
    }

    return false;
  }

  function calcGroundSpeedSquared() {
    var vel = bot.entity.velocity;
    return vel.x * vel.x + vel.y * vel.y;
  }

  function getBoundingBox() {
    var pos = bot.entity.position;
    return {
      min: new Vec3(
        pos.x - physics.playerApothem,
        pos.y,
        pos.z - physics.playerApothem
      ).floor(),
      max: new Vec3(
        pos.x + physics.playerApothem,
        pos.y + physics.playerHeight,
        pos.z + physics.playerApothem
      ).floor(),
    };
  }

  function sendPositionAndLook(entity) {
    // sends data, no logic
    var packet = {
      x: entity.position.x,
      y: entity.position.y,
      z: entity.position.z,
      stance: entity.position.y + entity.height,
      onGround: entity.onGround,<commit_msg>physics: fix bug: walking too slowly on Z axis<commit_after>var Vec3 = require('vec3').Vec3
  , assert = require('assert')
  , math = require('../math')
  , conv = require('../conversions')

module.exports = inject;

var EPSILON = 0.000001
  , PI = Math.PI
  , PI_2 = Math.PI * 2
  , POSITION_UPDATE_INTERVAL_MS = 50
  , PHYSICS_INTERVAL_MS = 20

function inject(bot) {
  var physics = {
    maxGroundSpeed: 4.27, // according to the internet
    terminalVelocity: 20.0, // guess
    walkingAcceleration: 100.0, // seems good
    gravity: 27.0, // seems good
    groundFriction: 0.9, // seems good
    playerApothem: 0.32, // notch's client F3 says 0.30, but that caused spankings
    playerHeight: 1.74, // tested with a binary search
    jumpSpeed: 9.0, // seems good
    yawSpeed: 3.0, // seems good
    sprintSpeed: 1.3, // correct
  };

  var controlState = {
    forward: false,
    back: false,
    left: false,
    right: false,
    jump: false,
    sprint: false,
  };
  var jumpQueued = false;
  var lastSentYaw = null;
  var positionUpdateTimer = null;
  var doPhysicsTimer = null;
  var lastPositionSentTime = null;
  var lastPhyicsFrameTime = null;

  function doPhysics() {
    var now = new Date();
    var deltaSeconds = (now - lastPhyicsFrameTime) / 1000;
    lastPhyicsFrameTime = now;
    nextFrame(deltaSeconds);
  }

  function cleanup() {
    stopPhysics();
    stopPositionUpdates();
  }

  function stopPositionUpdates() {
    clearInterval(positionUpdateTimer);
    positionUpdateTimer = null;
  }

  function stopPhysics() {
    clearInterval(doPhysicsTimer);
    doPhysicsTimer = null;
  }

  function nextFrame(deltaSeconds) {
    if (deltaSeconds < EPSILON) return; // too fast
    var pos = bot.entity.position;
    var vel = bot.entity.velocity;

    // derive xy movement vector from controls
    var movementRight = 0;
    if (controlState.right) movementRight += 1;
    if (controlState.left) movementRight -= 1;
    var movementForward = 0;
    if (controlState.forward) movementForward += 1;
    if (controlState.back) movementForward -= 1;

    // acceleration is m/s/s
    var acceleration = new Vec3(0, 0, 0);
    if (movementForward || movementRight) {
      // input acceleration
      var rotationFromInput = Math.atan2(-movementRight, movementForward);
      var inputYaw = bot.entity.yaw + rotationFromInput;
      acceleration.x += physics.walkingAcceleration * -Math.sin(inputYaw);
      acceleration.z += physics.walkingAcceleration * -Math.cos(inputYaw);
      if (controlState.sprint) {
        acceleration.x *= physics.sprintSpeed;
        acceleration.z *= physics.sprintSpeed;
      }
    }

    // jumping
    if ((controlState.jump || jumpQueued) && bot.entity.onGround) {
      vel.y = physics.jumpSpeed;
    }
    jumpQueued = false;

    // gravity
    acceleration.y -= physics.gravity;

    var oldGroundSpeedSquared = calcGroundSpeedSquared();
    if (oldGroundSpeedSquared < EPSILON) {
      // stopped
      vel.x = 0;
      vel.z = 0;
    } else {
      // non-zero ground speed
      var oldGroundSpeed = Math.sqrt(oldGroundSpeedSquared);
      var groundFriction = physics.groundFriction * physics.walkingAcceleration;
      // less friction for air
      if (! bot.entity.onGround) groundFriction *= 0.05;
      // if friction would stop the motion, do it
      var maybeNewGroundFriction = oldGroundSpeed / deltaSeconds;
      groundFriction = groundFriction > maybeNewGroundFriction ?
        maybeNewGroundFriction : groundFriction;
      acceleration.x -= vel.x / oldGroundSpeed * groundFriction;
      acceleration.z -= vel.z / oldGroundSpeed * groundFriction;
    }

    // calculate new speed
    vel.add(acceleration.scaled(deltaSeconds))

    // limit speed
    var groundSpeedSquared = calcGroundSpeedSquared();
    if (groundSpeedSquared > physics.maxGroundSpeed * physics.maxGroundSpeed) {
      var groundSpeed = Math.sqrt(groundSpeedSquared);
      var correctionScale = physics.maxGroundSpeed / groundSpeed;
      vel.x *= correctionScale;
      vel.z *= correctionScale;
    }
    vel.y = math.clamp(-physics.terminalVelocity, vel.y, physics.terminalVelocity);

    // calculate new positions and resolve collisions
    var boundingBox = getBoundingBox();
    var boundingBoxMin, boundingBoxMax;
    if (vel.x !== 0) {
      pos.x += vel.x * deltaSeconds;
      var blockX = Math.floor(pos.x + math.sign(vel.x) * physics.playerApothem);
      boundingBoxMin = new Vec3(blockX, boundingBox.min.y, boundingBox.min.z);
      boundingBoxMax = new Vec3(blockX, boundingBox.max.y, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.x = blockX + (vel.x < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.x = 0;
        boundingBox = getBoundingBox();
      }
    }

    if (vel.z !== 0) {
      pos.z += vel.z * deltaSeconds;
      var blockZ = Math.floor(pos.z + math.sign(vel.z) * physics.playerApothem);
      boundingBoxMin = new Vec3(boundingBox.min.x, boundingBox.min.y, blockZ);
      boundingBoxMax = new Vec3(boundingBox.max.x, boundingBox.max.y, blockZ);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.z = blockZ + (vel.z < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.z = 0;
        boundingBox = getBoundingBox();
      }
    }


    bot.entity.onGround = false;
    if (vel.y !== 0) {
      pos.y += vel.y * deltaSeconds;
      var playerHalfHeight = physics.playerHeight / 2;
      var blockY = Math.floor(pos.y + playerHalfHeight + math.sign(vel.y) * playerHalfHeight);
      boundingBoxMin = new Vec3(boundingBox.min.x, blockY, boundingBox.min.z);
      boundingBoxMax = new Vec3(boundingBox.max.x, blockY, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.y = blockY + (vel.y < 0 ? 1 : -physics.playerHeight) * 1.001;
        bot.entity.onGround = vel.y < 0 ? true : bot.entity.onGround;
        vel.y = 0;
      }
    }

    bot.emit('move');
  }

  function collisionInRange(boundingBoxMin, boundingBoxMax) {
    var cursor = new Vec3(0, 0, 0);
    var block;
    for (cursor.x = boundingBoxMin.x; cursor.x <= boundingBoxMax.x; cursor.x++) {
      for (cursor.y = boundingBoxMin.y; cursor.y <= boundingBoxMax.y; cursor.y++) {
        for (cursor.z = boundingBoxMin.z; cursor.z <= boundingBoxMax.z; cursor.z++) {
          block = bot.blockAt(cursor);
          if (block && block.boundingBox === 'block') return true;
        }
      }
    }

    return false;
  }

  function calcGroundSpeedSquared() {
    var vel = bot.entity.velocity;
    return vel.x * vel.x + vel.z * vel.z;
  }

  function getBoundingBox() {
    var pos = bot.entity.position;
    return {
      min: new Vec3(
        pos.x - physics.playerApothem,
        pos.y,
        pos.z - physics.playerApothem
      ).floor(),
      max: new Vec3(
        pos.x + physics.playerApothem,
        pos.y + physics.playerHeight,
        pos.z + physics.playerApothem
      ).floor(),
    };
  }

  function sendPositionAndLook(entity) {
    // sends data, no logic
    var packet = {
      x: entity.position.x,
      y: entity.position.y,
      z: entity.position.z,
      stance: entity.position.y + entity.height,
      onGround: entity.onGround,
{'fraction_non_alphanumeric': 0.11962282331887948, 'fraction_numerical': 0.014453850918851952, 'mean_word_length': 2.679412509496075, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5965890', 'n_tokens_mistral': 4992, 'n_tokens_neox': 4691, 'n_words': 1069}