Deterministic packets (#11014)
This commit is contained in:
+42
-12
@@ -1201,19 +1201,49 @@ void PhoneAPI::prefetchNodeInfos()
|
|||||||
onNowHasData(0);
|
onNowHasData(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/// Derive a stable id for a replayed satellite-DB record. Unchanged history replayed on
|
||||||
|
/// every reconnect must not look like a new packet to the phone's history/dedup - the id
|
||||||
|
/// only changes when the underlying data (its timestamp) actually changes.
|
||||||
|
// `kind` only needs to distinguish the record types that can otherwise collide (e.g. device
|
||||||
|
// vs. environment metrics both replay as TELEMETRY_APP with the same node/last_heard) - pass
|
||||||
|
// the payload's own variant/port constant.
|
||||||
|
uint32_t makeReplayPacketId(NodeNum num, uint32_t timestamp, uint32_t kind)
|
||||||
|
{
|
||||||
|
uint32_t h = num;
|
||||||
|
h = h * 2654435761u + timestamp;
|
||||||
|
h = h * 2654435761u + kind;
|
||||||
|
return h ? h : 1; // some clients treat id 0 as "unset"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populate hop_start/hop_limit from the node's real last-known hop count (if any) so a
|
||||||
|
/// replayed packet doesn't read as "heard directly" when it wasn't.
|
||||||
|
void setReplayHopFields(meshtastic_MeshPacket &pkt, const meshtastic_NodeInfoLite *header)
|
||||||
|
{
|
||||||
|
uint8_t hopLimit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
||||||
|
uint8_t hopsAway = (header && header->has_hops_away) ? header->hops_away : 0;
|
||||||
|
pkt.hop_start = hopLimit;
|
||||||
|
pkt.hop_limit = hopsAway < hopLimit ? (uint8_t)(hopLimit - hopsAway) : 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
meshtastic_MeshPacket PhoneAPI::makeReplayPositionPacket(NodeNum num, const meshtastic_PositionLite &pos)
|
meshtastic_MeshPacket PhoneAPI::makeReplayPositionPacket(NodeNum num, const meshtastic_PositionLite &pos)
|
||||||
{
|
{
|
||||||
// Shape this exactly like a fresh live broadcast Position from the peer so the
|
// Shape this exactly like a fresh live broadcast Position from the peer so the
|
||||||
// phone runs it through its normal "live position broadcast" handler path.
|
// phone runs it through its normal "live position broadcast" handler path.
|
||||||
// to=ourNum would read as a DM-from-peer and never lands in node detail UI.
|
// to=ourNum would read as a DM-from-peer and never lands in node detail UI.
|
||||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||||
|
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||||
pkt.from = num;
|
pkt.from = num;
|
||||||
pkt.to = NODENUM_BROADCAST;
|
pkt.to = NODENUM_BROADCAST;
|
||||||
pkt.id = generatePacketId();
|
|
||||||
pkt.rx_time = pos.time;
|
pkt.rx_time = pos.time;
|
||||||
|
// Stable per-node/per-fix id: replaying the same unchanged history on every
|
||||||
|
// reconnect must not look like a brand new packet to the phone's history/dedup.
|
||||||
|
pkt.id = makeReplayPacketId(num, pkt.rx_time, meshtastic_PortNum_POSITION_APP);
|
||||||
pkt.channel = 0;
|
pkt.channel = 0;
|
||||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
pkt.rx_snr = header ? header->snr : 0;
|
||||||
pkt.hop_start = pkt.hop_limit;
|
setReplayHopFields(pkt, header);
|
||||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||||
// Mark as if heard over the air, not internally generated
|
// Mark as if heard over the air, not internally generated
|
||||||
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
||||||
@@ -1231,13 +1261,13 @@ meshtastic_MeshPacket PhoneAPI::makeReplayTelemetryPacket(NodeNum num, const mes
|
|||||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||||
pkt.from = num;
|
pkt.from = num;
|
||||||
pkt.to = NODENUM_BROADCAST;
|
pkt.to = NODENUM_BROADCAST;
|
||||||
pkt.id = generatePacketId();
|
|
||||||
// No native timestamp on telemetry packets here; use last_heard.
|
// No native timestamp on telemetry packets here; use last_heard.
|
||||||
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||||
pkt.rx_time = header ? header->last_heard : 0;
|
pkt.rx_time = header ? header->last_heard : 0;
|
||||||
|
pkt.id = makeReplayPacketId(num, pkt.rx_time, meshtastic_Telemetry_device_metrics_tag);
|
||||||
pkt.channel = 0;
|
pkt.channel = 0;
|
||||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
pkt.rx_snr = header ? header->snr : 0;
|
||||||
pkt.hop_start = pkt.hop_limit;
|
setReplayHopFields(pkt, header);
|
||||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||||
// Mark as if heard over the air, not internally generated - iOS client filters
|
// Mark as if heard over the air, not internally generated - iOS client filters
|
||||||
// TRANSPORT_INTERNAL packets out of broadcast peer state updates.
|
// TRANSPORT_INTERNAL packets out of broadcast peer state updates.
|
||||||
@@ -1337,12 +1367,12 @@ meshtastic_MeshPacket PhoneAPI::makeReplayEnvironmentPacket(uint32_t num, const
|
|||||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||||
pkt.from = num;
|
pkt.from = num;
|
||||||
pkt.to = NODENUM_BROADCAST;
|
pkt.to = NODENUM_BROADCAST;
|
||||||
pkt.id = generatePacketId();
|
|
||||||
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||||
pkt.rx_time = header ? header->last_heard : 0;
|
pkt.rx_time = header ? header->last_heard : 0;
|
||||||
|
pkt.id = makeReplayPacketId(num, pkt.rx_time, meshtastic_Telemetry_environment_metrics_tag);
|
||||||
pkt.channel = 0;
|
pkt.channel = 0;
|
||||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
pkt.rx_snr = header ? header->snr : 0;
|
||||||
pkt.hop_start = pkt.hop_limit;
|
setReplayHopFields(pkt, header);
|
||||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||||
// Mark as if heard over the air, not internally generated - iOS client filters
|
// Mark as if heard over the air, not internally generated - iOS client filters
|
||||||
// TRANSPORT_INTERNAL packets out of broadcast peer state updates.
|
// TRANSPORT_INTERNAL packets out of broadcast peer state updates.
|
||||||
@@ -1400,13 +1430,13 @@ meshtastic_MeshPacket PhoneAPI::makeReplayStatusPacket(uint32_t num, const mesht
|
|||||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||||
pkt.from = num;
|
pkt.from = num;
|
||||||
pkt.to = NODENUM_BROADCAST;
|
pkt.to = NODENUM_BROADCAST;
|
||||||
pkt.id = generatePacketId();
|
|
||||||
// StatusMessage has no native timestamp; use last_heard.
|
// StatusMessage has no native timestamp; use last_heard.
|
||||||
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||||
pkt.rx_time = header ? header->last_heard : 0;
|
pkt.rx_time = header ? header->last_heard : 0;
|
||||||
|
pkt.id = makeReplayPacketId(num, pkt.rx_time, meshtastic_PortNum_NODE_STATUS_APP);
|
||||||
pkt.channel = 0;
|
pkt.channel = 0;
|
||||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
pkt.rx_snr = header ? header->snr : 0;
|
||||||
pkt.hop_start = pkt.hop_limit;
|
setReplayHopFields(pkt, header);
|
||||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||||
// Mark as if heard over the air, not internally generated - client filters
|
// Mark as if heard over the air, not internally generated - client filters
|
||||||
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
||||||
|
|||||||
Reference in New Issue
Block a user