{"action":"created","comment":{"url":"https://api.github.com/repos/minetest/minetest/pulls/comments/199385499","pull_request_review_id":133493109,"id":199385499,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5OTM4NTQ5OQ==","diff_hunk":"@@ -141,6 +145,1107 @@ void Player::clearHud()\n \t}\n }\n \n+// FROM ClientEnvironment::step\n+void Player::step(float dtime, const PlayerControl &control,\n+\t\tconst PlayerSettings &player_settings, Environment *env,\n+\t\tstd::vector<CollisionInfo> *player_collisions)\n+{\n+\t// Get some settings\n+\tbool fly_allowed = checkPrivilege(\"fly\");\n+\tbool free_move = fly_allowed && player_settings.free_move;\n+\n+\tf32 player_speed = getSpeed().getLength();\n+\n+\t/*\n+\t\tMaximum position increment\n+\t*/\n+\t//f32 position_max_increment = 0.05*BS;\n+\tf32 position_max_increment = 0.1*BS;\n+\n+\t// Maximum time increment (for collision detection etc)\n+\t// time = distance / speed\n+\tf32 dtime_max_increment = 1;\n+\tif(player_speed > 0.001)\n+\t\tdtime_max_increment = position_max_increment / player_speed;\n+\n+\t// Maximum time increment is 10ms or lower\n+\tif(dtime_max_increment > 0.01)\n+\t\tdtime_max_increment = 0.01;\n+\n+\tf32 dtime_downcount = dtime;\n+\n+\t/*\n+\t\tStuff that has a maximum time increment\n+\t*/\n+\n+\tu32 loopcount = 0;\n+\tdo\n+\t{\n+\t\tloopcount++;\n+\n+\t\tf32 dtime_part;\n+\t\tif(dtime_downcount > dtime_max_increment)\n+\t\t{\n+\t\t\tdtime_part = dtime_max_increment;\n+\t\t\tdtime_downcount -= dtime_part;\n+\t\t}\n+\t\telse\n+\t\t{\n+\t\t\tdtime_part = dtime_downcount;\n+\t\t\t/*\n+\t\t\t\tSetting this to 0 (no -=dtime_part) disables an infinite loop\n+\t\t\t\twhen dtime_part is so small that dtime_downcount -= dtime_part\n+\t\t\t\tdoes nothing\n+\t\t\t*/\n+\t\t\tdtime_downcount = 0;\n+\t\t}\n+\n+\t\t/*\n+\t\t\tHandle local player\n+\t\t*/\n+\n+\t\t{\n+\t\t\t// Apply physics\n+\t\t\tif(!free_move && !is_climbing)\n+\t\t\t{\n+\t\t\t\t// Gravity\n+\t\t\t\tv3f speed = getSpeed();\n+\t\t\t\tif(!in_liquid)\n+\t\t\t\t\tspeed.Y -= movement_gravity * physics_override_gravity * dtime_part * 2;\n+\n+\t\t\t\t// Liquid floating / sinking\n+\t\t\t\tif(in_liquid && !swimming_vertical)\n+\t\t\t\t\tspeed.Y -= movement_liquid_sink * dtime_part * 2;\n+\n+\t\t\t\t// Liquid resistance\n+\t\t\t\tif(in_liquid_stable || in_liquid)\n+\t\t\t\t{\n+\t\t\t\t\t// How much the node's viscosity blocks movement, ranges between 0 and 1\n+\t\t\t\t\t// Should match the scale at which viscosity increase affects other liquid attributes\n+\t\t\t\t\tconst f32 viscosity_factor = 0.3;\n+\n+\t\t\t\t\tv3f d_wanted = -speed / movement_liquid_fluidity;\n+\t\t\t\t\tf32 dl = d_wanted.getLength();\n+\t\t\t\t\tif(dl > movement_liquid_fluidity_smooth)\n+\t\t\t\t\t\tdl = movement_liquid_fluidity_smooth;\n+\t\t\t\t\tdl *= (liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);\n+\n+\t\t\t\t\tv3f d = d_wanted.normalize() * dl;\n+\t\t\t\t\tspeed += d;\n+\t\t\t\t}\n+\n+\t\t\t\tsetSpeed(speed);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t\tMove the player.\n+\t\t\t\tThis also does collision detection.\n+\t\t\t*/\n+\t\t\tmove(dtime_part, control, player_settings, env,\n+\t\t\t\t\tposition_max_increment, player_collisions);\n+\t\t}\n+\t}\n+\twhile(dtime_downcount > 0.001);\n+\n+\t//std::cout<<\"Looped \"<<loopcount<<\" times.\"<<std::endl;\n+\n+}\n+\n+// FROM LocalPlayer\n+void Player::applyControl(float dtime, const PlayerControl &control,\n+\t\tconst PlayerSettings &player_settings, Environment *env)\n+{\n+\t// Clear stuff\n+\tswimming_vertical = false;\n+\n+\tsetPitch(control.pitch);\n+\tsetYaw(control.yaw);\n+\n+\t// Nullify speed and don't run positioning code if the player is attached\n+\tif(isAttached)\n+\t{\n+\t\tsetSpeed(v3f(0,0,0));\n+\t\treturn;\n+\t}\n+\n+\tv3f move_direction = v3f(0,0,1);\n+\tmove_direction.rotateXZBy(getYaw());\n+\n+\tv3f speedH = v3f(0,0,0); // Horizontal (X, Z)\n+\tv3f speedV = v3f(0,0,0); // Vertical (Y)\n+\n+\tbool fly_allowed = checkPrivilege(\"fly\");\n+\tbool fast_allowed = checkPrivilege(\"fast\");\n+\n+\tbool free_move = fly_allowed && player_settings.free_move;\n+\tbool fast_move = fast_allowed && player_settings.fast_move;\n+\t// When aux1_descends is enabled the fast key is used to go down, so fast isn't possible\n+\tbool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;\n+\tbool continuous_forward = player_settings.continuous_forward;\n+\tbool always_fly_fast = player_settings.always_fly_fast;\n+\n+\t// Whether superspeed mode is used or not\n+\tbool superspeed = false;\n+\n+\tif (always_fly_fast && free_move && fast_move)\n+\t\tsuperspeed = true;\n+\n+\t// Old descend control\n+\tif(player_settings.aux1_descends)\n+\t{\n+\t\t// If free movement and fast movement, always move fast\n+\t\tif(free_move && fast_move)\n+\t\t\tsuperspeed = true;\n+\n+\t\t// Auxiliary button 1 (E)\n+\t\tif(control.aux1)\n+\t\t{\n+\t\t\tif(free_move)\n+\t\t\t{\n+\t\t\t\t// In free movement mode, aux1 descends\n+\t\t\t\tif(fast_move)\n+\t\t\t\t\tspeedV.Y = -movement_speed_fast;\n+\t\t\t\telse\n+\t\t\t\t\tspeedV.Y = -movement_speed_walk;\n+\t\t\t}\n+\t\t\telse if(in_liquid || in_liquid_stable)\n+\t\t\t{\n+\t\t\t\tspeedV.Y = -movement_speed_walk;\n+\t\t\t\tswimming_vertical = true;\n+\t\t\t}\n+\t\t\telse if(is_climbing)\n+\t\t\t{\n+\t\t\t\tspeedV.Y = -movement_speed_climb;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t{\n+\t\t\t\t// If not free movement but fast is allowed, aux1 is\n+\t\t\t\t// \"Turbo button\"\n+\t\t\t\tif(fast_move)\n+\t\t\t\t\tsuperspeed = true;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t// New minecraft-like descend control\n+\telse\n+\t{\n+\t\t// Auxiliary button 1 (E)\n+\t\tif(control.aux1)\n+\t\t{\n+\t\t\tif(!is_climbing)\n+\t\t\t{\n+\t\t\t\t// aux1 is \"Turbo button\"\n+\t\t\t\tif(fast_move)\n+\t\t\t\t\tsuperspeed = true;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif(control.sneak)\n+\t\t{\n+\t\t\tif(free_move)\n+\t\t\t{\n+\t\t\t\t// In free movement mode, sneak descends\n+\t\t\t\tif (fast_move && (control.aux1 || always_fly_fast))\n+\t\t\t\t\tspeedV.Y = -movement_speed_fast;\n+\t\t\t\telse\n+\t\t\t\t\tspeedV.Y = -movement_speed_walk;\n+\t\t\t}\n+\t\t\telse if(in_liquid || in_liquid_stable)\n+\t\t\t{\n+\t\t\t\tif(fast_climb)\n+\t\t\t\t\tspeedV.Y = -movement_speed_fast;\n+\t\t\t\telse\n+\t\t\t\t\tspeedV.Y = -movement_speed_walk;\n+\t\t\t\tswimming_vertical = true;\n+\t\t\t}\n+\t\t\telse if(is_climbing)\n+\t\t\t{\n+\t\t\t\tif(fast_climb)\n+\t\t\t\t\tspeedV.Y = -movement_speed_fast;\n+\t\t\t\telse\n+\t\t\t\t\tspeedV.Y = -movement_speed_climb;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (continuous_forward)\n+\t\tspeedH += move_direction;\n+\n+\tif (control.up) {\n+\t\tif (continuous_forward) {\n+\t\t\tif (fast_move)\n+\t\t\t\tsuperspeed = true;\n+\t\t} else {\n+\t\t\tspeedH += move_direction;\n+\t\t}\n+\t}\n+\tif (control.down) {\n+\t\tspeedH -= move_direction;\n+\t}\n+\tif (!control.up && !control.down) {\n+\t\tspeedH -= move_direction *\n+\t\t\t(control.forw_move_joystick_axis / 32767.f);\n+\t}\n+\tif (control.left) {\n+\t\tspeedH += move_direction.crossProduct(v3f(0,1,0));\n+\t}\n+\tif (control.right) {\n+\t\tspeedH += move_direction.crossProduct(v3f(0,-1,0));\n+\t}\n+\tif (!control.left && !control.right) {\n+\t\tspeedH -= move_direction.crossProduct(v3f(0,1,0)) *\n+\t\t\t(control.sidew_move_joystick_axis / 32767.f);\n+\t}\n+\tif(control.jump)\n+\t{\n+\t\tif (free_move) {\n+\t\t\tif (player_settings.aux1_descends || always_fly_fast) {\n+\t\t\t\tif (fast_move)\n+\t\t\t\t\tspeedV.Y = movement_speed_fast;\n+\t\t\t\telse\n+\t\t\t\t\tspeedV.Y = movement_speed_walk;\n+\t\t\t} else {\n+\t\t\t\tif(fast_move && control.aux1)\n+\t\t\t\t\tspeedV.Y = movement_speed_fast;\n+\t\t\t\telse\n+\t\t\t\t\tspeedV.Y = movement_speed_walk;\n+\t\t\t}\n+\t\t}\n+\t\telse if(m_can_jump)\n+\t\t{\n+\t\t\t/*\n+\t\t\t\tNOTE: The d value in move() affects jump height by\n+\t\t\t\traising the height at which the jump speed is kept\n+\t\t\t\tat its starting value\n+\t\t\t*/\n+\t\t\tv3f speedJ = getSpeed();\n+\t\t\tif(speedJ.Y >= -0.5 * BS) {\n+\t\t\t\tspeedJ.Y = movement_speed_jump * physics_override_jump;\n+\t\t\t\tsetSpeed(speedJ);\n+\t\t\t\ttriggerJumpEvent();\n+\t\t\t}\n+\t\t}\n+\t\telse if(in_liquid)\n+\t\t{\n+\t\t\tif(fast_climb)\n+\t\t\t\tspeedV.Y = movement_speed_fast;\n+\t\t\telse\n+\t\t\t\tspeedV.Y = movement_speed_walk;\n+\t\t\tswimming_vertical = true;\n+\t\t}\n+\t\telse if(is_climbing)\n+\t\t{\n+\t\t\tif(fast_climb)\n+\t\t\t\tspeedV.Y = movement_speed_fast;\n+\t\t\telse\n+\t\t\t\tspeedV.Y = movement_speed_climb;\n+\t\t}\n+\t}\n+\n+\t// The speed of the player (Y is ignored)\n+\tif(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))\n+\t\tspeedH = speedH.normalize() * movement_speed_fast;\n+\telse if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)\n+\t\tspeedH = speedH.normalize() * movement_speed_crouch;\n+\telse\n+\t\tspeedH = speedH.normalize() * movement_speed_walk;\n+\n+\t// Acceleration increase\n+\tf32 incH = 0; // Horizontal (X, Z)\n+\tf32 incV = 0; // Vertical (Y)\n+\tif((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))\n+\t{\n+\t\t// Jumping and falling\n+\t\tif(superspeed || (fast_move && control.aux1))\n+\t\t\tincH = movement_acceleration_fast * BS * dtime;\n+\t\telse\n+\t\t\tincH = movement_acceleration_air * BS * dtime;\n+\t\tincV = 0; // No vertical acceleration in air\n+\t}\n+\telse if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))\n+\t\tincH = incV = movement_acceleration_fast * BS * dtime;\n+\telse\n+\t\tincH = incV = movement_acceleration_default * BS * dtime;\n+\n+\tfloat slip_factor = 1.0f;\n+\tif (!free_move)\n+\t\tslip_factor = getSlipFactor(env, speedH);\n+\n+\t// Accelerate to target speed with maximum increment\n+\taccelerateHorizontal(speedH * physics_override_speed,\n+\t\t\tincH * physics_override_speed * slip_factor);\n+\taccelerateVertical(speedV * physics_override_speed,\n+\t\t\tincV * physics_override_speed);\n+}\n+\n+// FROM LocalPlayer\n+void Player::move(f32 dtime, const PlayerControl &control,\n+\t\tconst PlayerSettings &player_settings, Environment *env,\n+\t\tf32 pos_max_d,\n+\t\tstd::vector<CollisionInfo> *collision_info)\n+{\n+\tif (!collision_info || collision_info->empty()) {\n+\t\t// Node below the feet, update each ClientEnvironment::step()\n+\t\tm_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);\n+\t}\n+\n+\t// Temporary option for old move code\n+\tif (!physics_override_new_move) {\n+\t\told_move(dtime, control, player_settings, env, pos_max_d, collision_info);\n+\t\treturn;\n+\t}\n+\n+\tMap *map = &env->getMap();\n+\t// TODO: check for NULL?\n+\tconst NodeDefManager *nodemgr = getNodeDefManager();\n+\n+\tv3f position = getPosition();\n+\n+\t// Copy parent position if local player is attached\n+\tif (isAttached) {\n+\t\thandleAttachedMove();\n+\t\treturn;\n+\t}\n+\n+\t// Skip collision detection if noclip mode is used\n+\tbool fly_allowed = checkPrivilege(\"fly\");\n+\tbool noclip = checkPrivilege(\"noclip\") && player_settings.noclip;\n+\tbool free_move = player_settings.free_move && fly_allowed;\n+\n+\tif (noclip && free_move) {\n+\t\tposition += m_speed * dtime;\n+\t\tsetPosition(position);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t\tCollision detection\n+\t*/\n+\n+\tbool is_valid_position;\n+\tMapNode node;\n+\tv3s16 pp;\n+\n+\t/*\n+\t\tCheck if player is in liquid (the oscillating value)\n+\t*/\n+\n+\t// If in liquid, the threshold of coming out is at higher y\n+\tif (in_liquid)\n+\t{\n+\t\tpp = floatToInt(position + v3f(0,BS*0.1,0), BS);\n+\t\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\t\tif (is_valid_position) {\n+\t\t\tin_liquid = nodemgr->get(node.getContent()).isLiquid();\n+\t\t\tliquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;\n+\t\t} else {\n+\t\t\tin_liquid = false;\n+\t\t}\n+\t}\n+\t// If not in liquid, the threshold of going in is at lower y\n+\telse\n+\t{\n+\t\tpp = floatToInt(position + v3f(0,BS*0.5,0), BS);\n+\t\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\t\tif (is_valid_position) {\n+\t\t\tin_liquid = nodemgr->get(node.getContent()).isLiquid();\n+\t\t\tliquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;\n+\t\t} else {\n+\t\t\tin_liquid = false;\n+\t\t}\n+\t}\n+\n+\n+\t/*\n+\t\tCheck if player is in liquid (the stable value)\n+\t*/\n+\tpp = floatToInt(position + v3f(0,0,0), BS);\n+\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\tif (is_valid_position) {\n+\t\tin_liquid_stable = nodemgr->get(node.getContent()).isLiquid();\n+\t} else {\n+\t\tin_liquid_stable = false;\n+\t}\n+\n+\t/*\n+\t        Check if player is climbing\n+\t*/\n+\n+\n+\tpp = floatToInt(position + v3f(0,0.5*BS,0), BS);\n+\tv3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);\n+\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\tbool is_valid_position2;\n+\tMapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);\n+\n+\tif (!(is_valid_position && is_valid_position2)) {\n+\t\tis_climbing = false;\n+\t} else {\n+\t\tis_climbing = (nodemgr->get(node.getContent()).climbable\n+\t\t\t\t|| nodemgr->get(node2.getContent()).climbable) && !free_move;\n+\t}\n+\n+\t/*\n+\t\tCollision uncertainty radius\n+\t\tMake it a bit larger than the maximum distance of movement\n+\t*/\n+\t//f32 d = pos_max_d * 1.1;\n+\t// A fairly large value in here makes moving smoother\n+\tf32 d = 0.15*BS;\n+\n+\t// This should always apply, otherwise there are glitches\n+\tsanity_check(d > pos_max_d);\n+\n+\t// Player object property step height is multiplied by BS in\n+\t// /src/script/common/c_content.cpp and /src/content_sao.cpp\n+\tfloat player_stepheight = getStepHeight();\n+\n+\t// TODO this is a problematic hack.\n+\t// Use a better implementation for autojump, or apply a custom stepheight\n+\t// to all players, as this currently creates unintended special movement\n+\t// abilities and advantages for Android players on a server.\n+#ifdef __ANDROID__\n+\tif (touching_ground)\n+\t\tplayer_stepheight += (0.6f * BS);\n+#endif\n+\n+\tv3f accel_f = v3f(0,0,0);\n+\n+\tcollisionMoveResult result = collisionMoveSimple(env, env->getGameDef(),\n+\t\tpos_max_d, m_collisionbox, player_stepheight, dtime,\n+\t\t&position, &m_speed, accel_f);\n+\n+\tbool could_sneak = control.sneak && !free_move && !in_liquid &&\n+\t\t!is_climbing && physics_override_sneak;\n+\n+\t// Add new collisions to the vector\n+\tif (collision_info && !free_move) {\n+\t\tv3f diff = intToFloat(m_standing_node, BS) - position;\n+\t\tf32 distance = diff.getLength();\n+\t\t// Force update each ClientEnvironment::step()\n+\t\tbool is_first = collision_info->empty();\n+\n+\t\tfor (const auto &colinfo : result.collisions) {\n+\t\t\tcollision_info->push_back(colinfo);\n+\n+\t\t\tif (colinfo.type != COLLISION_NODE ||\n+\t\t\t\t\tcolinfo.new_speed.Y != 0 ||\n+\t\t\t\t\t(could_sneak && m_sneak_node_exists))\n+\t\t\t\tcontinue;\n+\n+\t\t\tdiff = intToFloat(colinfo.node_p, BS) - position;\n+\n+\t\t\t// Find nearest colliding node\n+\t\t\tf32 len = diff.getLength();\n+\t\t\tif (is_first || len < distance) {\n+\t\t\t\tm_standing_node = colinfo.node_p;\n+\t\t\t\tdistance = len;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/*\n+\t\tIf the player's feet touch the topside of any node, this is\n+\t\tset to true.\n+\n+\t\tPlayer is allowed to jump when this is true.\n+\t*/\n+\tbool touching_ground_was = touching_ground;\n+\ttouching_ground = result.touching_ground;\n+\tbool sneak_can_jump = false;\n+\n+\t// Max. distance (X, Z) over border for sneaking determined by collision box\n+\t// * 0.49 to keep the center just barely on the node\n+\tv3f sneak_max = m_collisionbox.getExtent() * 0.49;\n+\n+\tif (m_sneak_ladder_detected) {\n+\t\t// restore legacy behaviour (this makes the m_speed.Y hack necessary)\n+\t\tsneak_max = v3f(0.4 * BS, 0, 0.4 * BS);\n+\t}\n+\n+\t/*\n+\t\tIf sneaking, keep on top of last walked node and don't fall off\n+\t*/\n+\tif (could_sneak && m_sneak_node_exists) {\n+\t\tconst v3f sn_f = intToFloat(m_sneak_node, BS);\n+\t\tconst v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;\n+\t\tconst v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;\n+\t\tconst v3f old_pos = position;\n+\t\tconst v3f old_speed = m_speed;\n+\t\tf32 y_diff = bmax.Y - position.Y;\n+\t\tm_standing_node = m_sneak_node;\n+\n+\t\t// (BS * 0.6f) is the basic stepheight while standing on ground\n+\t\tif (y_diff < BS * 0.6f) {\n+\t\t\t// Only center player when they're on the node\n+\t\t\tposition.X = rangelim(position.X,\n+\t\t\t\tbmin.X - sneak_max.X, bmax.X + sneak_max.X);\n+\t\t\tposition.Z = rangelim(position.Z,\n+\t\t\t\tbmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);\n+\n+\t\t\tif (position.X != old_pos.X)\n+\t\t\t\tm_speed.X = 0;\n+\t\t\tif (position.Z != old_pos.Z)\n+\t\t\t\tm_speed.Z = 0;\n+\t\t}\n+\n+\t\tif (y_diff > 0 && m_speed.Y < 0 &&\n+\t\t\t\t(physics_override_sneak_glitch || y_diff < BS * 0.6f)) {\n+\t\t\t// Move player to the maximal height when falling or when\n+\t\t\t// the ledge is climbed on the next step.\n+\t\t\tposition.Y = bmax.Y;\n+\t\t\tm_speed.Y = 0;\n+\t\t}\n+\n+\t\t// Allow jumping on node edges while sneaking\n+\t\tif (m_speed.Y == 0 || m_sneak_ladder_detected)\n+\t\t\tsneak_can_jump = true;\n+\n+\t\tif (collision_info &&\n+\t\t\t\tm_speed.Y - old_speed.Y > BS) {\n+\t\t\t// Collide with sneak node, report fall damage\n+\t\t\tCollisionInfo sn_info;\n+\t\t\tsn_info.node_p = m_sneak_node;\n+\t\t\tsn_info.old_speed = old_speed;\n+\t\t\tsn_info.new_speed = m_speed;\n+\t\t\tcollision_info->push_back(sn_info);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t\tFind the next sneak node if necessary\n+\t*/\n+\tbool new_sneak_node_exists = false;\n+\n+\tif (could_sneak)\n+\t\tnew_sneak_node_exists = updateSneakNode(map, position, sneak_max);\n+\n+\t/*\n+\t\tSet new position but keep sneak node set\n+\t*/\n+\tsetPosition(position);\n+\tm_sneak_node_exists = new_sneak_node_exists;\n+\n+\t/*\n+\t\tReport collisions\n+\t*/\n+\n+\tif(!result.standing_on_object && !touching_ground_was && touching_ground) {\n+\t\treportRegainGround();\n+\t}\n+\n+\tcalculateCameraInCeiling(map, nodemgr);\n+\n+\t/*\n+\t\tCheck properties of the node on which the player is standing\n+\t*/\n+\tconst ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));\n+\t// Determine if jumping is possible\n+\tm_can_jump = (touching_ground && !in_liquid && !is_climbing)\n+\t\t\t|| sneak_can_jump;\n+\tif (itemgroup_get(f.groups, \"disable_jump\"))\n+\t\tm_can_jump = false;\n+\n+\t// Jump key pressed while jumping off from a bouncy block\n+\tif (m_can_jump && control.jump && itemgroup_get(f.groups, \"bouncy\") &&\n+\t\tm_speed.Y >= -0.5 * BS) {\n+\t\tfloat jumpspeed = movement_speed_jump * physics_override_jump;\n+\t\tif (m_speed.Y > 1) {\n+\t\t\t// Reduce boost when speed already is high\n+\t\t\tm_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));\n+\t\t} else {\n+\t\t\tm_speed.Y += jumpspeed;\n+\t\t}\n+\t\tsetSpeed(m_speed);\n+\t\tm_can_jump = false;\n+\t}\n+}\n+\n+// FROM LocalPlayer\n+void Player::old_move(f32 dtime, const PlayerControl &control,\n+\t\tconst PlayerSettings &player_settings, Environment *env, f32 pos_max_d,\n+\t\tstd::vector<CollisionInfo> *collision_info)\n+{\n+\tMap *map = &env->getMap();\n+\t// TODO: check for NULL?\n+\tconst NodeDefManager *nodemgr = getNodeDefManager();\n+\n+\tv3f position = getPosition();\n+\n+\t// Copy parent position if local player is attached\n+\tif (isAttached) {\n+\t\thandleAttachedMove();\n+\t\tm_sneak_node_exists = false;\n+\t\treturn;\n+\t}\n+\n+\t// Skip collision detection if noclip mode is used\n+\tbool fly_allowed = checkPrivilege(\"fly\");\n+\tbool noclip = checkPrivilege(\"noclip\") && player_settings.noclip;\n+\tbool free_move = noclip && fly_allowed && player_settings.free_move;\n+\n+\tif (free_move) {\n+\t\tposition += m_speed * dtime;\n+\t\tsetPosition(position);\n+\t\tm_sneak_node_exists = false;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t\tCollision detection\n+\t*/\n+\tbool is_valid_position;\n+\tMapNode node;\n+\tv3s16 pp;\n+\n+\t/*\n+\t\tCheck if player is in liquid (the oscillating value)\n+\t*/\n+\tif (in_liquid) {\n+\t\t// If in liquid, the threshold of coming out is at higher y\n+\t\tpp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);\n+\t\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\t\tif (is_valid_position) {\n+\t\t\tin_liquid = nodemgr->get(node.getContent()).isLiquid();\n+\t\t\tliquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;\n+\t\t} else {\n+\t\t\tin_liquid = false;\n+\t\t}\n+\t} else {\n+\t\t// If not in liquid, the threshold of going in is at lower y\n+\t\tpp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);\n+\t\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\t\tif (is_valid_position) {\n+\t\t\tin_liquid = nodemgr->get(node.getContent()).isLiquid();\n+\t\t\tliquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;\n+\t\t} else {\n+\t\t\tin_liquid = false;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t\tCheck if player is in liquid (the stable value)\n+\t*/\n+\tpp = floatToInt(position + v3f(0, 0, 0), BS);\n+\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\tif (is_valid_position)\n+\t\tin_liquid_stable = nodemgr->get(node.getContent()).isLiquid();\n+\telse\n+\t\tin_liquid_stable = false;\n+\n+\t/*\n+\t\tCheck if player is climbing\n+\t*/\n+\tpp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);\n+\tv3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);\n+\tnode = map->getNodeNoEx(pp, &is_valid_position);\n+\tbool is_valid_position2;\n+\tMapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);\n+\n+\tif (!(is_valid_position && is_valid_position2))\n+\t\tis_climbing = false;\n+\telse\n+\t\tis_climbing = (nodemgr->get(node.getContent()).climbable ||\n+\t\t\t\tnodemgr->get(node2.getContent()).climbable) && !free_move;\n+\n+\t/*\n+\t\tCollision uncertainty radius\n+\t\tMake it a bit larger than the maximum distance of movement\n+\t*/\n+\t//f32 d = pos_max_d * 1.1;\n+\t// A fairly large value in here makes moving smoother\n+\tf32 d = 0.15 * BS;\n+\t// This should always apply, otherwise there are glitches\n+\tsanity_check(d > pos_max_d);\n+\t// Maximum distance over border for sneaking\n+\tf32 sneak_max = BS * 0.4;\n+\n+\t/*\n+\t\tIf sneaking, keep in range from the last walked node and don't\n+\t\tfall off from it\n+\t*/\n+\tif (control.sneak && m_sneak_node_exists &&\n+\t\t\t!(fly_allowed && player_settings.free_move) && !in_liquid &&\n+\t\t\tphysics_override_sneak) {\n+\t\tf32 maxd = 0.5 * BS + sneak_max;\n+\t\tv3f lwn_f = intToFloat(m_sneak_node, BS);\n+\t\tposition.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);\n+\t\tposition.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);\n+\n+\t\tif (!is_climbing) {\n+\t\t\t// Move up if necessary\n+\t\t\tf32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;\n+\t\t\tif (position.Y < new_y)\n+\t\t\t\tposition.Y = new_y;\n+\t\t\t/*\n+\t\t\t\tCollision seems broken, since player is sinking when\n+\t\t\t\tsneaking over the edges of current sneaking_node.\n+\t\t\t\tTODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.\n+\t\t\t*/\n+\t\t\tif (m_speed.Y < 0)\n+\t\t\t\tm_speed.Y = 0;\n+\t\t}\n+\t}\n+\n+\t// this shouldn't be hardcoded but transmitted from server\n+\tfloat player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);\n+\n+#ifdef __ANDROID__\n+\tplayer_stepheight += (0.6 * BS);\n+#endif\n+\n+\tv3f accel_f = v3f(0, 0, 0);\n+\n+\tcollisionMoveResult result = collisionMoveSimple(env, env->getGameDef(),\n+\t\tpos_max_d, m_collisionbox, player_stepheight, dtime,\n+\t\t&position, &m_speed, accel_f);\n+\n+\t/*\n+\t\tIf the player's feet touch the topside of any node, this is\n+\t\tset to true.\n+\n+\t\tPlayer is allowed to jump when this is true.\n+\t*/\n+\tbool touching_ground_was = touching_ground;\n+\ttouching_ground = result.touching_ground;\n+\n+    //bool standing_on_unloaded = result.standing_on_unloaded;\n+\n+\t/*\n+\t\tCheck the nodes under the player to see from which node the\n+\t\tplayer is sneaking from, if any.  If the node from under\n+\t\tthe player has been removed, the player falls.\n+\t*/\n+\tf32 position_y_mod = 0.05 * BS;\n+\tif (m_sneak_node_bb_ymax > 0)\n+\t\tposition_y_mod = m_sneak_node_bb_ymax - position_y_mod;\n+\tv3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);\n+\tif (m_sneak_node_exists &&\n+\t\t\tnodemgr->get(map->getNodeNoEx(m_old_node_below)).name == \"air\" &&\n+\t\t\tm_old_node_below_type != \"air\") {\n+\t\t// Old node appears to have been removed; that is,\n+\t\t// it wasn't air before but now it is\n+\t\tm_need_to_get_new_sneak_node = false;\n+\t\tm_sneak_node_exists = false;\n+\t} else if (nodemgr->get(map->getNodeNoEx(current_node)).name != \"air\") {\n+\t\t// We are on something, so make sure to recalculate the sneak\n+\t\t// node.\n+\t\tm_need_to_get_new_sneak_node = true;\n+\t}\n+\n+\tif (m_need_to_get_new_sneak_node && physics_override_sneak) {\n+\t\tm_sneak_node_bb_ymax = 0;\n+\t\tv3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);\n+\t\tv2f player_p2df(position.X, position.Z);\n+\t\tf32 min_distance_f = 100000.0 * BS;\n+\t\t// If already seeking from some node, compare to it.\n+\t\tv3s16 new_sneak_node = m_sneak_node;\n+\t\tfor (s16 x= -1; x <= 1; x++)\n+\t\tfor (s16 z= -1; z <= 1; z++) {\n+\t\t\tv3s16 p = pos_i_bottom + v3s16(x, 0, z);\n+\t\t\tv3f pf = intToFloat(p, BS);\n+\t\t\tv2f node_p2df(pf.X, pf.Z);\n+\t\t\tf32 distance_f = player_p2df.getDistanceFrom(node_p2df);\n+\t\t\tf32 max_axis_distance_f = MYMAX(\n+\t\t\t\t\tstd::fabs(player_p2df.X - node_p2df.X),\n+\t\t\t\t\tstd::fabs(player_p2df.Y - node_p2df.Y));\n+\n+\t\t\tif (distance_f > min_distance_f ||\n+\t\t\t\t\tmax_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)\n+\t\t\t\tcontinue;\n+\n+\t\t\t// The node to be sneaked on has to be walkable\n+\t\t\tnode = map->getNodeNoEx(p, &is_valid_position);\n+\t\t\tif (!is_valid_position || !nodemgr->get(node).walkable)\n+\t\t\t\tcontinue;\n+\t\t\t// And the node above it has to be nonwalkable\n+\t\t\tnode = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);\n+\t\t\tif (!is_valid_position || nodemgr->get(node).walkable)\n+\t\t\t\tcontinue;\n+\t\t\t// If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable\n+\t\t\tif (!physics_override_sneak_glitch) {\n+\t\t\t\tnode =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);\n+\t\t\t\tif (!is_valid_position || nodemgr->get(node).walkable)\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tmin_distance_f = distance_f;\n+\t\t\tnew_sneak_node = p;\n+\t\t}\n+\n+\t\tbool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);\n+\n+\t\tm_sneak_node = new_sneak_node;\n+\t\tm_sneak_node_exists = sneak_node_found;\n+\n+\t\tif (sneak_node_found) {\n+\t\t\tf32 cb_max = 0;\n+\t\t\tMapNode n = map->getNodeNoEx(m_sneak_node);\n+\t\t\tstd::vector<aabb3f> nodeboxes;\n+\t\t\tn.getCollisionBoxes(nodemgr, &nodeboxes);\n+\t\t\tfor (const auto &box : nodeboxes) {\n+\t\t\t\tif (box.MaxEdge.Y > cb_max)\n+\t\t\t\t\tcb_max = box.MaxEdge.Y;\n+\t\t\t}\n+\t\t\tm_sneak_node_bb_ymax = cb_max;\n+\t\t}\n+\n+\t\t/*\n+\t\t\tIf sneaking, the player's collision box can be in air, so\n+\t\t\tthis has to be set explicitly\n+\t\t*/\n+\t\tif (sneak_node_found && control.sneak)\n+\t\t\ttouching_ground = true;\n+\t}\n+\n+\t/*\n+\t\tSet new position but keep sneak node set\n+\t*/\n+\tbool sneak_node_exists = m_sneak_node_exists;\n+\tsetPosition(position);\n+\tm_sneak_node_exists = sneak_node_exists;\n+\n+\t/*\n+\t\tReport collisions\n+\t*/\n+\t// Dont report if flying\n+\tif (collision_info && !(player_settings.free_move && fly_allowed)) {\n+\t\tfor (const auto &info : result.collisions) {\n+\t\t\tcollision_info->push_back(info);\n+\t\t}\n+\t}\n+\n+\tif (!result.standing_on_object && !touching_ground_was && touching_ground) {\n+\t\treportRegainGround();\n+\t}\n+\n+\tcalculateCameraInCeiling(map, nodemgr);\n+\n+\t/*\n+\t\tUpdate the node last under the player\n+\t*/\n+\tm_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);\n+\tm_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;\n+\n+\t/*\n+\t\tCheck properties of the node on which the player is standing\n+\t*/\n+\tconst ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));\n+\t// Determine if jumping is possible\n+\tm_can_jump = touching_ground && !in_liquid;\n+\tif (itemgroup_get(f.groups, \"disable_jump\"))\n+\t\tm_can_jump = false;\n+\t// Jump key pressed while jumping off from a bouncy block\n+\tif (m_can_jump && control.jump && itemgroup_get(f.groups, \"bouncy\") &&\n+\t\t\tm_speed.Y >= -0.5 * BS) {\n+\t\tfloat jumpspeed = movement_speed_jump * physics_override_jump;\n+\t\tif (m_speed.Y > 1) {\n+\t\t\t// Reduce boost when speed already is high\n+\t\t\tm_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));\n+\t\t} else {\n+\t\t\tm_speed.Y += jumpspeed;\n+\t\t}\n+\t\tsetSpeed(m_speed);\n+\t\tm_can_jump = false;\n+\t}\n+}\n+\n+// FROM LocalPlayer\n+// Horizontal acceleration (X and Z), Y direction is ignored\n+void Player::accelerateHorizontal(const v3f &target_speed,\n+\tconst f32 max_increase)\n+{\n+        if (max_increase == 0)\n+                return;\n+\n+\tv3f d_wanted = target_speed - m_speed;\n+\td_wanted.Y = 0.0f;\n+\tf32 dl = d_wanted.getLength();\n+\tif (dl > max_increase)\n+\t\tdl = max_increase;\n+\n+\tv3f d = d_wanted.normalize() * dl;\n+\n+\tm_speed.X += d.X;\n+\tm_speed.Z += d.Z;\n+}\n+\n+// FROM LocalPlayer\n+// Vertical acceleration (Y), X and Z directions are ignored\n+void Player::accelerateVertical(const v3f &target_speed, const f32 max_increase)\n+{\n+\tif (max_increase == 0)\n+\t\treturn;\n+\n+\tf32 d_wanted = target_speed.Y - m_speed.Y;\n+\tif (d_wanted > max_increase)\n+\t\td_wanted = max_increase;\n+\telse if (d_wanted < -max_increase)\n+\t\td_wanted = -max_increase;\n+\n+\tm_speed.Y += d_wanted;\n+}\n+\n+// FROM LocalPlayer\n+float Player::getSlipFactor(Environment *env, const v3f &speedH)\n+{\n+\t// Slip on slippery nodes\n+\tconst NodeDefManager *nodemgr = env->getGameDef()->ndef();\n+\tMap *map = &env->getMap();\n+\tconst ContentFeatures &f = nodemgr->get(map->getNodeNoEx(\n+\t\t\tgetStandingNodePos()));\n+\tint slippery = 0;\n+\tif (f.walkable)\n+\t\tslippery = itemgroup_get(f.groups, \"slippery\");\n+\n+\tif (slippery >= 1) {\n+\t\tif (speedH == v3f(0.0f)) {\n+\t\t\tslippery = slippery * 2;\n+\t\t}","path":"src/player.cpp","position":null,"original_position":985,"commit_id":"ded2688e4aed68b39e535a9af0281a8f010609d1","original_commit_id":"f96195c2d1dbdfc39f2582c098d3ca45d24d41a3","user":{"login":"bendeutsch","id":2880643,"node_id":"MDQ6VXNlcjI4ODA2NDM=","avatar_url":"https://avatars2.githubusercontent.com/u/2880643?v=4","gravatar_id":"","url":"https://api.github.com/users/bendeutsch","html_url":"https://github.com/bendeutsch","followers_url":"https://api.github.com/users/bendeutsch/followers","following_url":"https://api.github.com/users/bendeutsch/following{/other_user}","gists_url":"https://api.github.com/users/bendeutsch/gists{/gist_id}","starred_url":"https://api.github.com/users/bendeutsch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bendeutsch/subscriptions","organizations_url":"https://api.github.com/users/bendeutsch/orgs","repos_url":"https://api.github.com/users/bendeutsch/repos","events_url":"https://api.github.com/users/bendeutsch/events{/privacy}","received_events_url":"https://api.github.com/users/bendeutsch/received_events","type":"User","site_admin":false},"body":"Done.","created_at":"2018-07-02T05:00:27Z","updated_at":"2018-07-02T05:00:28Z","html_url":"https://github.com/minetest/minetest/pull/7220#discussion_r199385499","pull_request_url":"https://api.github.com/repos/minetest/minetest/pulls/7220","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/minetest/minetest/pulls/comments/199385499"},"html":{"href":"https://github.com/minetest/minetest/pull/7220#discussion_r199385499"},"pull_request":{"href":"https://api.github.com/repos/minetest/minetest/pulls/7220"}},"in_reply_to_id":188224479},"pull_request":{"url":"https://api.github.com/repos/minetest/minetest/pulls/7220","id":180018578,"node_id":"MDExOlB1bGxSZXF1ZXN0MTgwMDE4NTc4","html_url":"https://github.com/minetest/minetest/pull/7220","diff_url":"https://github.com/minetest/minetest/pull/7220.diff","patch_url":"https://github.com/minetest/minetest/pull/7220.patch","issue_url":"https://api.github.com/repos/minetest/minetest/issues/7220","number":7220,"state":"open","locked":false,"title":"Move player movement code into Player class","user":{"login":"bendeutsch","id":2880643,"node_id":"MDQ6VXNlcjI4ODA2NDM=","avatar_url":"https://avatars2.githubusercontent.com/u/2880643?v=4","gravatar_id":"","url":"https://api.github.com/users/bendeutsch","html_url":"https://github.com/bendeutsch","followers_url":"https://api.github.com/users/bendeutsch/followers","following_url":"https://api.github.com/users/bendeutsch/following{/other_user}","gists_url":"https://api.github.com/users/bendeutsch/gists{/gist_id}","starred_url":"https://api.github.com/users/bendeutsch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bendeutsch/subscriptions","organizations_url":"https://api.github.com/users/bendeutsch/orgs","repos_url":"https://api.github.com/users/bendeutsch/repos","events_url":"https://api.github.com/users/bendeutsch/events{/privacy}","received_events_url":"https://api.github.com/users/bendeutsch/received_events","type":"User","site_admin":false},"body":"This allows running the same code on the server, for example for server-side movement checks.\r\n\r\nThis set of changes is actually a large part of pull request #6219 , \"server side movement\". But this pull request does not contain any of the network code or server-side checking or reconciliation. All it does is move the main movement methods from `LocalPlayer` to `Player` (and thus `RemotePlayer`), without changing any functionality.\r\n\r\nThere are just two points that are not \"pure\" in this sense:\r\n- the player attribute `control` has been changed to `m_control`, to avoid overshadowing by a function parameter of this name\r\n- the Android \"autojump via stepheight\" has, yet again, been removed and needs a replacement","created_at":"2018-04-06T18:15:25Z","updated_at":"2018-07-02T05:00:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1620d926e60a0b7613f39cc72e15eeff9cf1d16f","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":73368921,"node_id":"MDU6TGFiZWw3MzM2ODkyMQ==","url":"https://api.github.com/repos/minetest/minetest/labels/@%20Server%20/%20Client%20/%20Env.","name":"@ Server / Client / Env.","color":"0e8a16","default":false},{"id":338453186,"node_id":"MDU6TGFiZWwzMzg0NTMxODY=","url":"https://api.github.com/repos/minetest/minetest/labels/Action%20/%20change%20needed","name":"Action / change needed","color":"0052cc","default":false},{"id":91854503,"node_id":"MDU6TGFiZWw5MTg1NDUwMw==","url":"https://api.github.com/repos/minetest/minetest/labels/Maintenance","name":"Maintenance","color":"0e8a16","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/minetest/minetest/pulls/7220/commits","review_comments_url":"https://api.github.com/repos/minetest/minetest/pulls/7220/comments","review_comment_url":"https://api.github.com/repos/minetest/minetest/pulls/comments{/number}","comments_url":"https://api.github.com/repos/minetest/minetest/issues/7220/comments","statuses_url":"https://api.github.com/repos/minetest/minetest/statuses/ded2688e4aed68b39e535a9af0281a8f010609d1","head":{"label":"bendeutsch:lift-player-move","ref":"lift-player-move","sha":"ded2688e4aed68b39e535a9af0281a8f010609d1","user":{"login":"bendeutsch","id":2880643,"node_id":"MDQ6VXNlcjI4ODA2NDM=","avatar_url":"https://avatars2.githubusercontent.com/u/2880643?v=4","gravatar_id":"","url":"https://api.github.com/users/bendeutsch","html_url":"https://github.com/bendeutsch","followers_url":"https://api.github.com/users/bendeutsch/followers","following_url":"https://api.github.com/users/bendeutsch/following{/other_user}","gists_url":"https://api.github.com/users/bendeutsch/gists{/gist_id}","starred_url":"https://api.github.com/users/bendeutsch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bendeutsch/subscriptions","organizations_url":"https://api.github.com/users/bendeutsch/orgs","repos_url":"https://api.github.com/users/bendeutsch/repos","events_url":"https://api.github.com/users/bendeutsch/events{/privacy}","received_events_url":"https://api.github.com/users/bendeutsch/received_events","type":"User","site_admin":false},"repo":{"id":45211041,"node_id":"MDEwOlJlcG9zaXRvcnk0NTIxMTA0MQ==","name":"minetest","full_name":"bendeutsch/minetest","owner":{"login":"bendeutsch","id":2880643,"node_id":"MDQ6VXNlcjI4ODA2NDM=","avatar_url":"https://avatars2.githubusercontent.com/u/2880643?v=4","gravatar_id":"","url":"https://api.github.com/users/bendeutsch","html_url":"https://github.com/bendeutsch","followers_url":"https://api.github.com/users/bendeutsch/followers","following_url":"https://api.github.com/users/bendeutsch/following{/other_user}","gists_url":"https://api.github.com/users/bendeutsch/gists{/gist_id}","starred_url":"https://api.github.com/users/bendeutsch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bendeutsch/subscriptions","organizations_url":"https://api.github.com/users/bendeutsch/orgs","repos_url":"https://api.github.com/users/bendeutsch/repos","events_url":"https://api.github.com/users/bendeutsch/events{/privacy}","received_events_url":"https://api.github.com/users/bendeutsch/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bendeutsch/minetest","description":"Minetest, an open source infinite-world block sandbox game with survival and crafting. [also https://github.com/minetest/minetest_game]","fork":true,"url":"https://api.github.com/repos/bendeutsch/minetest","forks_url":"https://api.github.com/repos/bendeutsch/minetest/forks","keys_url":"https://api.github.com/repos/bendeutsch/minetest/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bendeutsch/minetest/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bendeutsch/minetest/teams","hooks_url":"https://api.github.com/repos/bendeutsch/minetest/hooks","issue_events_url":"https://api.github.com/repos/bendeutsch/minetest/issues/events{/number}","events_url":"https://api.github.com/repos/bendeutsch/minetest/events","assignees_url":"https://api.github.com/repos/bendeutsch/minetest/assignees{/user}","branches_url":"https://api.github.com/repos/bendeutsch/minetest/branches{/branch}","tags_url":"https://api.github.com/repos/bendeutsch/minetest/tags","blobs_url":"https://api.github.com/repos/bendeutsch/minetest/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bendeutsch/minetest/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bendeutsch/minetest/git/refs{/sha}","trees_url":"https://api.github.com/repos/bendeutsch/minetest/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bendeutsch/minetest/statuses/{sha}","languages_url":"https://api.github.com/repos/bendeutsch/minetest/languages","stargazers_url":"https://api.github.com/repos/bendeutsch/minetest/stargazers","contributors_url":"https://api.github.com/repos/bendeutsch/minetest/contributors","subscribers_url":"https://api.github.com/repos/bendeutsch/minetest/subscribers","subscription_url":"https://api.github.com/repos/bendeutsch/minetest/subscription","commits_url":"https://api.github.com/repos/bendeutsch/minetest/commits{/sha}","git_commits_url":"https://api.github.com/repos/bendeutsch/minetest/git/commits{/sha}","comments_url":"https://api.github.com/repos/bendeutsch/minetest/comments{/number}","issue_comment_url":"https://api.github.com/repos/bendeutsch/minetest/issues/comments{/number}","contents_url":"https://api.github.com/repos/bendeutsch/minetest/contents/{+path}","compare_url":"https://api.github.com/repos/bendeutsch/minetest/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bendeutsch/minetest/merges","archive_url":"https://api.github.com/repos/bendeutsch/minetest/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bendeutsch/minetest/downloads","issues_url":"https://api.github.com/repos/bendeutsch/minetest/issues{/number}","pulls_url":"https://api.github.com/repos/bendeutsch/minetest/pulls{/number}","milestones_url":"https://api.github.com/repos/bendeutsch/minetest/milestones{/number}","notifications_url":"https://api.github.com/repos/bendeutsch/minetest/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bendeutsch/minetest/labels{/name}","releases_url":"https://api.github.com/repos/bendeutsch/minetest/releases{/id}","deployments_url":"https://api.github.com/repos/bendeutsch/minetest/deployments","created_at":"2015-10-29T21:16:01Z","updated_at":"2016-06-12T20:08:30Z","pushed_at":"2018-07-02T04:53:38Z","git_url":"git://github.com/bendeutsch/minetest.git","ssh_url":"git@github.com:bendeutsch/minetest.git","clone_url":"https://github.com/bendeutsch/minetest.git","svn_url":"https://github.com/bendeutsch/minetest","homepage":"http://minetest.net/","size":58012,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"minetest:master","ref":"master","sha":"1ff15d3a46042d3432d9485249e4dc3b08dbc6ff","user":{"login":"minetest","id":2624745,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI2MjQ3NDU=","avatar_url":"https://avatars1.githubusercontent.com/u/2624745?v=4","gravatar_id":"","url":"https://api.github.com/users/minetest","html_url":"https://github.com/minetest","followers_url":"https://api.github.com/users/minetest/followers","following_url":"https://api.github.com/users/minetest/following{/other_user}","gists_url":"https://api.github.com/users/minetest/gists{/gist_id}","starred_url":"https://api.github.com/users/minetest/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/minetest/subscriptions","organizations_url":"https://api.github.com/users/minetest/orgs","repos_url":"https://api.github.com/users/minetest/repos","events_url":"https://api.github.com/users/minetest/events{/privacy}","received_events_url":"https://api.github.com/users/minetest/received_events","type":"Organization","site_admin":false},"repo":{"id":2168780,"node_id":"MDEwOlJlcG9zaXRvcnkyMTY4Nzgw","name":"minetest","full_name":"minetest/minetest","owner":{"login":"minetest","id":2624745,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI2MjQ3NDU=","avatar_url":"https://avatars1.githubusercontent.com/u/2624745?v=4","gravatar_id":"","url":"https://api.github.com/users/minetest","html_url":"https://github.com/minetest","followers_url":"https://api.github.com/users/minetest/followers","following_url":"https://api.github.com/users/minetest/following{/other_user}","gists_url":"https://api.github.com/users/minetest/gists{/gist_id}","starred_url":"https://api.github.com/users/minetest/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/minetest/subscriptions","organizations_url":"https://api.github.com/users/minetest/orgs","repos_url":"https://api.github.com/users/minetest/repos","events_url":"https://api.github.com/users/minetest/events{/privacy}","received_events_url":"https://api.github.com/users/minetest/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/minetest/minetest","description":"Minetest, an open source infinite-world block sandbox game engine with support for survival and crafting. Also see https://github.com/minetest/minetest_game","fork":false,"url":"https://api.github.com/repos/minetest/minetest","forks_url":"https://api.github.com/repos/minetest/minetest/forks","keys_url":"https://api.github.com/repos/minetest/minetest/keys{/key_id}","collaborators_url":"https://api.github.com/repos/minetest/minetest/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/minetest/minetest/teams","hooks_url":"https://api.github.com/repos/minetest/minetest/hooks","issue_events_url":"https://api.github.com/repos/minetest/minetest/issues/events{/number}","events_url":"https://api.github.com/repos/minetest/minetest/events","assignees_url":"https://api.github.com/repos/minetest/minetest/assignees{/user}","branches_url":"https://api.github.com/repos/minetest/minetest/branches{/branch}","tags_url":"https://api.github.com/repos/minetest/minetest/tags","blobs_url":"https://api.github.com/repos/minetest/minetest/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/minetest/minetest/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/minetest/minetest/git/refs{/sha}","trees_url":"https://api.github.com/repos/minetest/minetest/git/trees{/sha}","statuses_url":"https://api.github.com/repos/minetest/minetest/statuses/{sha}","languages_url":"https://api.github.com/repos/minetest/minetest/languages","stargazers_url":"https://api.github.com/repos/minetest/minetest/stargazers","contributors_url":"https://api.github.com/repos/minetest/minetest/contributors","subscribers_url":"https://api.github.com/repos/minetest/minetest/subscribers","subscription_url":"https://api.github.com/repos/minetest/minetest/subscription","commits_url":"https://api.github.com/repos/minetest/minetest/commits{/sha}","git_commits_url":"https://api.github.com/repos/minetest/minetest/git/commits{/sha}","comments_url":"https://api.github.com/repos/minetest/minetest/comments{/number}","issue_comment_url":"https://api.github.com/repos/minetest/minetest/issues/comments{/number}","contents_url":"https://api.github.com/repos/minetest/minetest/contents/{+path}","compare_url":"https://api.github.com/repos/minetest/minetest/compare/{base}...{head}","merges_url":"https://api.github.com/repos/minetest/minetest/merges","archive_url":"https://api.github.com/repos/minetest/minetest/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/minetest/minetest/downloads","issues_url":"https://api.github.com/repos/minetest/minetest/issues{/number}","pulls_url":"https://api.github.com/repos/minetest/minetest/pulls{/number}","milestones_url":"https://api.github.com/repos/minetest/minetest/milestones{/number}","notifications_url":"https://api.github.com/repos/minetest/minetest/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/minetest/minetest/labels{/name}","releases_url":"https://api.github.com/repos/minetest/minetest/releases{/id}","deployments_url":"https://api.github.com/repos/minetest/minetest/deployments","created_at":"2011-08-07T13:48:36Z","updated_at":"2018-07-01T11:48:18Z","pushed_at":"2018-07-02T04:53:41Z","git_url":"git://github.com/minetest/minetest.git","ssh_url":"git@github.com:minetest/minetest.git","clone_url":"https://github.com/minetest/minetest.git","svn_url":"https://github.com/minetest/minetest","homepage":"http://minetest.net/","size":58119,"stargazers_count":3319,"watchers_count":3319,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":910,"mirror_url":null,"archived":false,"open_issues_count":892,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":910,"open_issues":892,"watchers":3319,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/minetest/minetest/pulls/7220"},"html":{"href":"https://github.com/minetest/minetest/pull/7220"},"issue":{"href":"https://api.github.com/repos/minetest/minetest/issues/7220"},"comments":{"href":"https://api.github.com/repos/minetest/minetest/issues/7220/comments"},"review_comments":{"href":"https://api.github.com/repos/minetest/minetest/pulls/7220/comments"},"review_comment":{"href":"https://api.github.com/repos/minetest/minetest/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/minetest/minetest/pulls/7220/commits"},"statuses":{"href":"https://api.github.com/repos/minetest/minetest/statuses/ded2688e4aed68b39e535a9af0281a8f010609d1"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"bendeutsch"}}