Expose analog joystick input to the Lua API (#14348)

This commit is contained in:
grorp 2024-10-01 17:21:42 +02:00 committed by GitHub
parent 6569fdd4d1
commit 22ef4c8be1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 127 additions and 83 deletions

@ -8249,12 +8249,18 @@ child will follow movement and rotation of that bone.
bgcolor[], any non-style elements (eg: label) may result in weird behavior. bgcolor[], any non-style elements (eg: label) may result in weird behavior.
* Only affects formspecs shown after this is called. * Only affects formspecs shown after this is called.
* `get_formspec_prepend()`: returns a formspec string. * `get_formspec_prepend()`: returns a formspec string.
* `get_player_control()`: returns table with player pressed keys * `get_player_control()`: returns table with player input
* The table consists of fields with the following boolean values * The table contains the following boolean fields representing the pressed
representing the pressed keys: `up`, `down`, `left`, `right`, `jump`, keys: `up`, `down`, `left`, `right`, `jump`, `aux1`, `sneak`, `dig`,
`aux1`, `sneak`, `dig`, `place`, `LMB`, `RMB`, and `zoom`. `place`, `LMB`, `RMB` and `zoom`.
* The fields `LMB` and `RMB` are equal to `dig` and `place` respectively, * The fields `LMB` and `RMB` are equal to `dig` and `place` respectively,
and exist only to preserve backwards compatibility. and exist only to preserve backwards compatibility.
* The table also contains the fields `movement_x` and `movement_y`.
* They represent the movement of the player. Values are numbers in the
range [-1.0,+1.0].
* They take both keyboard and joystick input into account.
* You should prefer them over `up`, `down`, `left` and `right` to
support different input methods correctly.
* Returns an empty table `{}` if the object is not a player. * Returns an empty table `{}` if the object is not a player.
* `get_player_control_bits()`: returns integer with bit packed player pressed * `get_player_control_bits()`: returns integer with bit packed player pressed
keys. keys.

@ -1034,7 +1034,7 @@ void Client::Send(NetworkPacket* pkt)
m_con->Send(PEER_ID_SERVER, scf.channel, pkt, scf.reliable); m_con->Send(PEER_ID_SERVER, scf.channel, pkt, scf.reliable);
} }
// Will fill up 12 + 12 + 4 + 4 + 4 + 1 + 1 + 1 bytes // Will fill up 12 + 12 + 4 + 4 + 4 + 1 + 1 + 1 + 4 + 4 bytes
void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt, bool camera_inverted) void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt, bool camera_inverted)
{ {
v3f pf = myplayer->getPosition() * 100; v3f pf = myplayer->getPosition() * 100;
@ -1046,6 +1046,8 @@ void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *
u8 fov = std::fmin(255.0f, clientMap->getCameraFov() * 80.0f); u8 fov = std::fmin(255.0f, clientMap->getCameraFov() * 80.0f);
u8 wanted_range = std::fmin(255.0f, u8 wanted_range = std::fmin(255.0f,
std::ceil(clientMap->getWantedRange() * (1.0f / MAP_BLOCKSIZE))); std::ceil(clientMap->getWantedRange() * (1.0f / MAP_BLOCKSIZE)));
f32 movement_speed = myplayer->control.movement_speed;
f32 movement_dir = myplayer->control.movement_direction;
v3s32 position(pf.X, pf.Y, pf.Z); v3s32 position(pf.X, pf.Y, pf.Z);
v3s32 speed(sf.X, sf.Y, sf.Z); v3s32 speed(sf.X, sf.Y, sf.Z);
@ -1060,10 +1062,13 @@ void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *
[12+12+4+4+4] u8 fov*80 [12+12+4+4+4] u8 fov*80
[12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE) [12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE)
[12+12+4+4+4+1+1] u8 camera_inverted (bool) [12+12+4+4+4+1+1] u8 camera_inverted (bool)
[12+12+4+4+4+1+1+1] f32 movement_speed
[12+12+4+4+4+1+1+1+4] f32 movement_direction
*/ */
*pkt << position << speed << pitch << yaw << keyPressed; *pkt << position << speed << pitch << yaw << keyPressed;
*pkt << fov << wanted_range; *pkt << fov << wanted_range;
*pkt << camera_inverted; *pkt << camera_inverted;
*pkt << movement_speed << movement_dir;
} }
void Client::interact(InteractAction action, const PointedThing& pointed) void Client::interact(InteractAction action, const PointedThing& pointed)
@ -1397,6 +1402,8 @@ void Client::sendPlayerPos()
u32 keyPressed = player->control.getKeysPressed(); u32 keyPressed = player->control.getKeysPressed();
bool camera_inverted = m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT; bool camera_inverted = m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT;
f32 movement_speed = player->control.movement_speed;
f32 movement_dir = player->control.movement_direction;
if ( if (
player->last_position == player->getPosition() && player->last_position == player->getPosition() &&
@ -1406,7 +1413,9 @@ void Client::sendPlayerPos()
player->last_keyPressed == keyPressed && player->last_keyPressed == keyPressed &&
player->last_camera_fov == camera_fov && player->last_camera_fov == camera_fov &&
player->last_camera_inverted == camera_inverted && player->last_camera_inverted == camera_inverted &&
player->last_wanted_range == wanted_range) player->last_wanted_range == wanted_range &&
player->last_movement_speed == movement_speed &&
player->last_movement_dir == movement_dir)
return; return;
player->last_position = player->getPosition(); player->last_position = player->getPosition();
@ -1417,8 +1426,10 @@ void Client::sendPlayerPos()
player->last_camera_fov = camera_fov; player->last_camera_fov = camera_fov;
player->last_camera_inverted = camera_inverted; player->last_camera_inverted = camera_inverted;
player->last_wanted_range = wanted_range; player->last_wanted_range = wanted_range;
player->last_movement_speed = movement_speed;
player->last_movement_dir = movement_dir;
NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1 + 1); NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1 + 1 + 4 + 4);
writePlayerPos(player, &map, &pkt, camera_inverted); writePlayerPos(player, &map, &pkt, camera_inverted);

@ -2752,9 +2752,10 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
isKeyDown(KeyType::PLACE), isKeyDown(KeyType::PLACE),
cam.camera_pitch, cam.camera_pitch,
cam.camera_yaw, cam.camera_yaw,
input->getMovementSpeed(), input->getJoystickSpeed(),
input->getMovementDirection() input->getJoystickDirection()
); );
control.setMovementFromKeys();
// autoforward if set: move at maximum speed // autoforward if set: move at maximum speed
if (player->getPlayerSettings().continuous_forward && if (player->getPlayerSettings().continuous_forward &&

@ -220,48 +220,19 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
/* /*
* RealInputHandler * RealInputHandler
*/ */
float RealInputHandler::getMovementSpeed() float RealInputHandler::getJoystickSpeed()
{ {
bool f = m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]), if (g_touchcontrols && g_touchcontrols->getJoystickSpeed())
b = m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]), return g_touchcontrols->getJoystickSpeed();
l = m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]),
r = m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]);
if (f || b || l || r)
{
// if contradictory keys pressed, stay still
if (f && b && l && r)
return 0.0f;
else if (f && b && !l && !r)
return 0.0f;
else if (!f && !b && l && r)
return 0.0f;
return 1.0f; // If there is a keyboard event, assume maximum speed
}
if (g_touchcontrols && g_touchcontrols->getMovementSpeed())
return g_touchcontrols->getMovementSpeed();
return joystick.getMovementSpeed(); return joystick.getMovementSpeed();
} }
float RealInputHandler::getMovementDirection() float RealInputHandler::getJoystickDirection()
{ {
float x = 0, z = 0; // `getJoystickDirection() == 0` means forward, so we cannot use
// `getJoystickDirection()` as a condition.
/* Check keyboard for input */ if (g_touchcontrols && g_touchcontrols->getJoystickSpeed())
if (m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD])) return g_touchcontrols->getJoystickDirection();
z += 1;
if (m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]))
z -= 1;
if (m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]))
x += 1;
if (m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]))
x -= 1;
if (x != 0 || z != 0) /* If there is a keyboard event, it takes priority */
return std::atan2(x, z);
// `getMovementDirection() == 0` means forward, so we cannot use
// `getMovementDirection()` as a condition.
else if (g_touchcontrols && g_touchcontrols->getMovementSpeed())
return g_touchcontrols->getMovementDirection();
return joystick.getMovementDirection(); return joystick.getMovementDirection();
} }
@ -320,25 +291,11 @@ void RandomInputHandler::step(float dtime)
counterMovement -= dtime; counterMovement -= dtime;
if (counterMovement < 0.0) { if (counterMovement < 0.0) {
counterMovement = 0.1 * Rand(1, 40); counterMovement = 0.1 * Rand(1, 40);
movementSpeed = Rand(0,100)*0.01; joystickSpeed = Rand(0,100)*0.01;
movementDirection = Rand(-100, 100)*0.01 * M_PI; joystickDirection = Rand(-100, 100)*0.01 * M_PI;
} }
} else { } else {
bool f = keydown[keycache.key[KeyType::FORWARD]], joystickSpeed = 0.0f;
l = keydown[keycache.key[KeyType::LEFT]]; joystickDirection = 0.0f;
if (f || l) {
movementSpeed = 1.0f;
if (f && !l)
movementDirection = 0.0;
else if (!f && l)
movementDirection = -M_PI_2;
else if (f && l)
movementDirection = -M_PI_4;
else
movementDirection = 0.0;
} else {
movementSpeed = 0.0;
movementDirection = 0.0;
}
} }
} }

@ -247,8 +247,8 @@ public:
virtual bool wasKeyReleased(GameKeyType k) = 0; virtual bool wasKeyReleased(GameKeyType k) = 0;
virtual bool cancelPressed() = 0; virtual bool cancelPressed() = 0;
virtual float getMovementSpeed() = 0; virtual float getJoystickSpeed() = 0;
virtual float getMovementDirection() = 0; virtual float getJoystickDirection() = 0;
virtual void clearWasKeyPressed() {} virtual void clearWasKeyPressed() {}
virtual void clearWasKeyReleased() {} virtual void clearWasKeyReleased() {}
@ -304,9 +304,9 @@ public:
return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k); return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k);
} }
virtual float getMovementSpeed(); virtual float getJoystickSpeed();
virtual float getMovementDirection(); virtual float getJoystickDirection();
virtual bool cancelPressed() virtual bool cancelPressed()
{ {
@ -388,8 +388,8 @@ public:
virtual bool wasKeyPressed(GameKeyType k) { return false; } virtual bool wasKeyPressed(GameKeyType k) { return false; }
virtual bool wasKeyReleased(GameKeyType k) { return false; } virtual bool wasKeyReleased(GameKeyType k) { return false; }
virtual bool cancelPressed() { return false; } virtual bool cancelPressed() { return false; }
virtual float getMovementSpeed() { return movementSpeed; } virtual float getJoystickSpeed() { return joystickSpeed; }
virtual float getMovementDirection() { return movementDirection; } virtual float getJoystickDirection() { return joystickDirection; }
virtual v2s32 getMousePos() { return mousepos; } virtual v2s32 getMousePos() { return mousepos; }
virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }
@ -403,6 +403,6 @@ private:
KeyList keydown; KeyList keydown;
v2s32 mousepos; v2s32 mousepos;
v2s32 mousespeed; v2s32 mousespeed;
float movementSpeed; float joystickSpeed;
float movementDirection; float joystickDirection;
}; };

@ -105,6 +105,8 @@ public:
u8 last_camera_fov = 0; u8 last_camera_fov = 0;
u8 last_wanted_range = 0; u8 last_wanted_range = 0;
bool last_camera_inverted = false; bool last_camera_inverted = false;
f32 last_movement_speed = 0.0f;
f32 last_movement_dir = 0.0f;
float camera_impact = 0.0f; float camera_impact = 0.0f;

@ -163,8 +163,8 @@ public:
*/ */
line3d<f32> getShootline() { return m_shootline; } line3d<f32> getShootline() { return m_shootline; }
float getMovementDirection() { return m_joystick_direction; } float getJoystickDirection() { return m_joystick_direction; }
float getMovementSpeed() { return m_joystick_speed; } float getJoystickSpeed() { return m_joystick_speed; }
void step(float dtime); void step(float dtime);
inline void setUseCrosshair(bool use_crosshair) { m_draw_crosshair = use_crosshair; } inline void setUseCrosshair(bool use_crosshair) { m_draw_crosshair = use_crosshair; }

@ -962,6 +962,8 @@ enum ToServerCommand : u16
[2+12+12+4+4+4] u8 fov*80 [2+12+12+4+4+4] u8 fov*80
[2+12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE) [2+12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE)
[2+12+12+4+4+4+1+1] u8 camera_inverted (bool) [2+12+12+4+4+4+1+1] u8 camera_inverted (bool)
[2+12+12+4+4+4+1+1+1] f32 movement_speed
[2+12+12+4+4+4+1+1+1+4] f32 movement_direction
*/ */

@ -477,12 +477,24 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
u8 bits = 0; // bits instead of bool so it is extensible later u8 bits = 0; // bits instead of bool so it is extensible later
*pkt >> keyPressed; *pkt >> keyPressed;
player->control.unpackKeysPressed(keyPressed);
*pkt >> f32fov; *pkt >> f32fov;
fov = (f32)f32fov / 80.0f; fov = (f32)f32fov / 80.0f;
*pkt >> wanted_range; *pkt >> wanted_range;
if (pkt->getRemainingBytes() >= 1) if (pkt->getRemainingBytes() >= 1)
*pkt >> bits; *pkt >> bits;
if (pkt->getRemainingBytes() >= 8) {
*pkt >> player->control.movement_speed;
*pkt >> player->control.movement_direction;
} else {
player->control.movement_speed = 0.0f;
player->control.movement_direction = 0.0f;
player->control.setMovementFromKeys();
}
v3f position((f32)ps.X / 100.0f, (f32)ps.Y / 100.0f, (f32)ps.Z / 100.0f); v3f position((f32)ps.X / 100.0f, (f32)ps.Y / 100.0f, (f32)ps.Z / 100.0f);
v3f speed((f32)ss.X / 100.0f, (f32)ss.Y / 100.0f, (f32)ss.Z / 100.0f); v3f speed((f32)ss.X / 100.0f, (f32)ss.Y / 100.0f, (f32)ss.Z / 100.0f);
@ -501,8 +513,6 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
playersao->setWantedRange(wanted_range); playersao->setWantedRange(wanted_range);
playersao->setCameraInverted(bits & 0x01); playersao->setCameraInverted(bits & 0x01);
player->control.unpackKeysPressed(keyPressed);
if (playersao->checkMovementCheat()) { if (playersao->checkMovementCheat()) {
// Call callbacks // Call callbacks
m_script->on_cheat(playersao, "moved_too_fast"); m_script->on_cheat(playersao, "moved_too_fast");

@ -173,6 +173,42 @@ u16 Player::getMaxHotbarItemcount()
return mainlist ? std::min(mainlist->getSize(), (u32) hud_hotbar_itemcount) : 0; return mainlist ? std::min(mainlist->getSize(), (u32) hud_hotbar_itemcount) : 0;
} }
void PlayerControl::setMovementFromKeys()
{
bool a_up = direction_keys & (1 << 0),
a_down = direction_keys & (1 << 1),
a_left = direction_keys & (1 << 2),
a_right = direction_keys & (1 << 3);
if (a_up || a_down || a_left || a_right) {
// if contradictory keys pressed, stay still
if (a_up && a_down && a_left && a_right)
movement_speed = 0.0f;
else if (a_up && a_down && !a_left && !a_right)
movement_speed = 0.0f;
else if (!a_up && !a_down && a_left && a_right)
movement_speed = 0.0f;
else
// If there is a keyboard event, assume maximum speed
movement_speed = 1.0f;
}
// Check keyboard for input
float x = 0, y = 0;
if (a_up)
y += 1;
if (a_down)
y -= 1;
if (a_left)
x -= 1;
if (a_right)
x += 1;
if (x != 0 || y != 0)
// If there is a keyboard event, it takes priority
movement_direction = std::atan2(x, y);
}
#ifndef SERVER #ifndef SERVER
u32 PlayerControl::getKeysPressed() const u32 PlayerControl::getKeysPressed() const
@ -231,6 +267,11 @@ void PlayerControl::unpackKeysPressed(u32 keypress_bits)
zoom = keypress_bits & (1 << 9); zoom = keypress_bits & (1 << 9);
} }
v2f PlayerControl::getMovement() const
{
return v2f(std::sin(movement_direction), std::cos(movement_direction)) * movement_speed;
}
static auto tie(const PlayerPhysicsOverride &o) static auto tie(const PlayerPhysicsOverride &o)
{ {
// Make sure to add new members to this list! // Make sure to add new members to this list!

@ -86,6 +86,11 @@ struct PlayerControl
movement_direction = a_movement_direction; movement_direction = a_movement_direction;
} }
// Sets movement_speed and movement_direction according to direction_keys
// if direction_keys != 0, otherwise leaves them unchanged to preserve
// joystick input.
void setMovementFromKeys();
#ifndef SERVER #ifndef SERVER
// For client use // For client use
u32 getKeysPressed() const; u32 getKeysPressed() const;
@ -94,6 +99,7 @@ struct PlayerControl
// For server use // For server use
void unpackKeysPressed(u32 keypress_bits); void unpackKeysPressed(u32 keypress_bits);
v2f getMovement() const;
u8 direction_keys = 0; u8 direction_keys = 0;
bool jump = false; bool jump = false;
@ -102,7 +108,7 @@ struct PlayerControl
bool zoom = false; bool zoom = false;
bool dig = false; bool dig = false;
bool place = false; bool place = false;
// Note: These four are NOT available on the server // Note: These two are NOT available on the server
float pitch = 0.0f; float pitch = 0.0f;
float yaw = 0.0f; float yaw = 0.0f;
float movement_speed = 0.0f; float movement_speed = 0.0f;

@ -260,12 +260,13 @@ int LuaLocalPlayer::l_get_control(lua_State *L)
set("zoom", c.zoom); set("zoom", c.zoom);
set("dig", c.dig); set("dig", c.dig);
set("place", c.place); set("place", c.place);
// Player movement in polar coordinates and non-binary speed
lua_pushnumber(L, c.movement_speed); v2f movement = c.getMovement();
lua_setfield(L, -2, "movement_speed"); lua_pushnumber(L, movement.X);
lua_pushnumber(L, c.movement_direction); lua_setfield(L, -2, "movement_x");
lua_setfield(L, -2, "movement_direction"); lua_pushnumber(L, movement.Y);
// Provide direction keys to ensure compatibility lua_setfield(L, -2, "movement_y");
set("up", c.direction_keys & (1 << 0)); set("up", c.direction_keys & (1 << 0));
set("down", c.direction_keys & (1 << 1)); set("down", c.direction_keys & (1 << 1));
set("left", c.direction_keys & (1 << 2)); set("left", c.direction_keys & (1 << 2));

@ -1622,6 +1622,13 @@ int ObjectRef::l_get_player_control(lua_State *L)
lua_setfield(L, -2, "dig"); lua_setfield(L, -2, "dig");
lua_pushboolean(L, control.place); lua_pushboolean(L, control.place);
lua_setfield(L, -2, "place"); lua_setfield(L, -2, "place");
v2f movement = control.getMovement();
lua_pushnumber(L, movement.X);
lua_setfield(L, -2, "movement_x");
lua_pushnumber(L, movement.Y);
lua_setfield(L, -2, "movement_y");
// Legacy fields to ensure mod compatibility // Legacy fields to ensure mod compatibility
lua_pushboolean(L, control.dig); lua_pushboolean(L, control.dig);
lua_setfield(L, -2, "LMB"); lua_setfield(L, -2, "LMB");