1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-30 19:22:14 +00:00
This commit is contained in:
HybridDog 2025-09-22 14:39:09 -07:00 committed by GitHub
commit cc833d563d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 63 additions and 88 deletions

View file

@ -803,17 +803,20 @@ enum ToServerCommand : u16
TOSERVER_INTERACT = 0x39, TOSERVER_INTERACT = 0x39,
/* /*
[0] u16 command u16 command
[2] u8 action u8 action (InteractAction)
[3] u16 item u16 item
[5] u32 length of the next item u32 length of the next item
[9] serialized PointedThing u8[length] serialized PointedThing
actions: v3s32 player position multiplied by 100 and converted to integers
0: start digging (from undersurface) or use v3s32 player speed multiplied by 100 and converted to integers
1: stop digging (all parameters ignored) s32 player pitch multiplied by 100 and converted to an integer
2: digging completed s32 player yaw multiplied by 100 and converted to an integer
3: place block or item (to abovesurface) u32 keyPressed
4: use item u8 fov
u8 wanted drawing range
optional:
u8 flags; it is 0x01 if the camera is inverted
*/ */
TOSERVER_REMOVED_SOUNDS = 0x3a, TOSERVER_REMOVED_SOUNDS = 0x3a,

View file

@ -871,15 +871,6 @@ static inline void getWieldedItem(const PlayerSAO *playersao, std::optional<Item
void Server::handleCommand_Interact(NetworkPacket *pkt) void Server::handleCommand_Interact(NetworkPacket *pkt)
{ {
/*
[0] u16 command
[2] u8 action
[3] u16 item
[5] u32 length of the next item (plen)
[9] serialized PointedThing
[9 + plen] player position information
*/
InteractAction action; InteractAction action;
u16 item_i; u16 item_i;
@ -909,13 +900,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
if (playersao->isDead()) { if (playersao->isDead()) {
actionstream << "Server: " << player->getName() actionstream << "Server: " << player->getName()
<< " tried to interact while dead; ignoring." << std::endl; << " tried to interact while dead; ignoring." << std::endl;
if (pointed.type == POINTEDTHING_NODE) { getClient(peer_id)->respondToInteraction(action, pointed, false);
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
client->SetBlockNotSent(blockpos);
}
// Call callbacks
m_script->on_cheat(playersao, "interacted_while_dead"); m_script->on_cheat(playersao, "interacted_while_dead");
return; return;
} }
@ -955,22 +940,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
if (!checkPriv(player->getName(), "interact")) { if (!checkPriv(player->getName(), "interact")) {
actionstream << player->getName() << " attempted to interact with " << actionstream << player->getName() << " attempted to interact with " <<
pointed.dump() << " without 'interact' privilege" << std::endl; pointed.dump() << " without 'interact' privilege" << std::endl;
getClient(peer_id)->respondToInteraction(action, pointed, false);
if (pointed.type != POINTEDTHING_NODE)
return;
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
// Digging completed -> under
if (action == INTERACT_DIGGING_COMPLETED) {
v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
client->SetBlockNotSent(blockpos);
}
// Placement -> above
else if (action == INTERACT_PLACE) {
v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface);
client->SetBlockNotSent(blockpos);
}
return; return;
} }
@ -998,12 +968,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
float d = playersao->getEyePosition().getDistanceFrom(target_pos); float d = playersao->getEyePosition().getDistanceFrom(target_pos);
if (!checkInteractDistance(player, d, pointed.dump())) { if (!checkInteractDistance(player, d, pointed.dump())) {
if (pointed.type == POINTEDTHING_NODE) { getClient(peer_id)->respondToInteraction(action, pointed, false);
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
client->SetBlockNotSent(blockpos);
}
return; return;
} }
} }
@ -1156,14 +1121,12 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
if (is_valid_dig && n.getContent() != CONTENT_IGNORE) if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
m_script->node_on_dig(p_under, n, playersao); m_script->node_on_dig(p_under, n, playersao);
v3s16 blockpos = getNodeBlockPos(p_under); // For whatever reason we assume that the client always predicts that a
RemoteClient *client = getClient(peer_id); // dug node is air irrespective of the node's node_dig_prediction
// Send unusual result (that is, node not being removed) bool prediction_success =
if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) m_env->getMap().getNode(p_under).getContent() == CONTENT_AIR;
// Re-send block to revert change on client-side getClient(peer_id)->respondToInteraction(action, pointed,
client->SetBlockNotSent(blockpos); prediction_success);
else
client->ResendBlockIfOnWire(blockpos);
return; return;
} // action == INTERACT_DIGGING_COMPLETED } // action == INTERACT_DIGGING_COMPLETED
@ -1207,24 +1170,11 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
SendInventory(player, true); SendInventory(player, true);
} }
if (pointed.type != POINTEDTHING_NODE) // Since we do not known if the client has predicted the node at
return; // pointed.above or pointed.under,
// we assume that a prediction is always wrong.
// If item has node placement prediction, always send the getClient(peer_id)->respondToInteraction(action, pointed,
// blocks to make sure the client knows what exactly happened !had_prediction);
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface);
v3s16 blockpos2 = getNodeBlockPos(pointed.node_undersurface);
if (had_prediction) {
client->SetBlockNotSent(blockpos);
if (blockpos2 != blockpos)
client->SetBlockNotSent(blockpos2);
} else {
client->ResendBlockIfOnWire(blockpos);
if (blockpos2 != blockpos)
client->ResendBlockIfOnWire(blockpos2);
}
return; return;
} // action == INTERACT_PLACE } // action == INTERACT_PLACE

View file

@ -66,14 +66,6 @@ RemoteClient::RemoteClient() :
{ {
} }
void RemoteClient::ResendBlockIfOnWire(v3s16 p)
{
// if this block is on wire, mark it for sending again as soon as possible
if (m_blocks_sending.find(p) != m_blocks_sending.end()) {
SetBlockNotSent(p);
}
}
static LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env) static LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env)
{ {
ServerActiveObject *ao = sao; ServerActiveObject *ao = sao;
@ -453,6 +445,30 @@ void RemoteClient::SetBlocksNotSent(const std::vector<v3s16> &blocks, bool low_p
} }
} }
void RemoteClient::respondToInteraction(InteractAction action,
const PointedThing &pointed, bool prediction_success)
{
if ((action != INTERACT_PLACE && action != INTERACT_DIGGING_COMPLETED)
|| pointed.type != POINTEDTHING_NODE)
// The client has not predicted not node changes
return;
// The client may have an outdated mapblock if the placement or dig
// prediction was wrong or if an old mapblock is still being sent to it.
v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
if (!prediction_success
|| m_blocks_sending.find(blockpos) != m_blocks_sending.end()) {
SetBlockNotSent(blockpos);
}
if (action != INTERACT_PLACE)
return;
v3s16 blockpos2 = getNodeBlockPos(pointed.node_abovesurface);
if (blockpos2 != blockpos && (!prediction_success
|| m_blocks_sending.find(blockpos2) != m_blocks_sending.end())) {
SetBlockNotSent(blockpos2);
}
}
void RemoteClient::notifyEvent(ClientStateEvent event) void RemoteClient::notifyEvent(ClientStateEvent event)
{ {
std::ostringstream myerror; std::ostringstream myerror;

View file

@ -11,6 +11,7 @@
#include "porting.h" #include "porting.h"
#include "threading/mutex_auto_lock.h" #include "threading/mutex_auto_lock.h"
#include "clientdynamicinfo.h" #include "clientdynamicinfo.h"
#include "util/pointedthing.h"
#include <list> #include <list>
#include <memory> #include <memory>
@ -255,12 +256,17 @@ public:
void SetBlocksNotSent(const std::vector<v3s16> &blocks, bool low_priority = false); void SetBlocksNotSent(const std::vector<v3s16> &blocks, bool low_priority = false);
/** /**
* tell client about this block being modified right now. * Trigger block sending to undo client-side predicted node changes
* this information is required to requeue the block in case it's "on wire" *
* while modification is processed by server * \param action The interact action to determine which predictions the
* @param p position of modified block * client could have made
* \param pointed The positions where the client has interacted
* \param prediction_success If true, assume that the client has made a
* correct prediction and send the mapblock only if an outdated mapblock
* is currently "on wire", which can erroneously override the prediction
*/ */
void ResendBlockIfOnWire(v3s16 p); void respondToInteraction(InteractAction action,
const PointedThing &pointed, bool prediction_success);
u32 getSendingCount() const { return m_blocks_sending.size(); } u32 getSendingCount() const { return m_blocks_sending.size(); }