Conversation
There was a problem hiding this comment.
Pull request overview
Adds variable-length, multi-byte routing-path support across the UI and path utilities so routing can scale beyond single-byte hop IDs, with documentation and tests to describe/validate the new behavior.
Changes:
- Introduces hop-aware path utilities (
splitPathBytes,formatHopHex) and updates name resolution to support multi-byte hop prefixes. - Updates path-related UI (custom path entry, path details, path trace/map views) to respect the device-reported
pathHashByteWidth. - Adds documentation for routing path encoding/capability detection and expands unit tests for path parsing/name resolution.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| windows/flutter/generated_plugins.cmake | Adds jni to the Windows FFI plugin list. |
| linux/flutter/generated_plugins.cmake | Adds jni to the Linux FFI plugin list. |
| lib/helpers/path_helper.dart | Adds multi-byte hop formatting/splitting and updates resolvePathNames to be width-aware. |
| test/helpers/path_helper_test.dart | Adds tests for hop-splitting and multi-byte path name resolution. |
| lib/widgets/path_selection_dialog.dart | Updates custom path entry/selection UI to parse and display hop prefixes using the configured hash width. |
| lib/widgets/path_management_dialog.dart | Passes pathHashByteWidth into path name resolution and the selection dialog. |
| lib/screens/chat_screen.dart | Uses width-aware path name resolution when showing full path details. |
| lib/screens/channel_message_path_screen.dart | Converts observed/reported path byte lengths into hop counts based on hash width; updates path display formatting accordingly. |
| lib/screens/path_trace_map.dart | Refactors path trace parsing/matching/labeling to handle multi-byte hop prefixes. |
| lib/services/notification_service.dart | Sanitizes notification identifiers/device-info logging strings for safer debug output. |
| documentation/routing-paths.md | New documentation describing multi-byte routing path encoding and UI/display conventions. |
| documentation/README.md | Adds the new “Routing Paths” documentation link. |
| documentation/chat-and-messaging.md | Documents multi-byte custom path entry format in the Chat UI. |
Comments suppressed due to low confidence (2)
lib/screens/path_trace_map.dart:870
formatDirectionSubText()can be called withindex == pathTraceData.pathData.length(the ListView usesitemCount: pathData.length + 1). In that case, the else-branch readspathData[index], which will throw a RangeError and crash for any non-empty trace. Handle the last UI row explicitly (e.g., treatindex == pathData.lengthas the final “you” endpoint) or adjust indexing sopathData[index]is never out of bounds.
} else {
final hop = pathTraceData.pathData[index];
final contactName = pathTraceData.pathContacts[_hopKey(hop)]?.name;
final hex = PathHelper.formatHopHex(hop);
return contactName != null
lib/screens/path_trace_map.dart:445
- Target inference compares the selected hop key to
c.path.last(single byte). With multi-byte paths this breaks matching and prevents inferring the target location from peers. Use the last hop chunk (width bytes) fromc.pathwhen building the comparison key.
(c) =>
c.hasLocation &&
c.path.isNotEmpty &&
_hopKey(Uint8List.fromList([c.path.last])) == lastHopKey,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| } | ||
| return traceBytes; | ||
| mirroredHops.addAll(pathHops.reversed); |
| c.path.isNotEmpty && | ||
| _hopKey(Uint8List.fromList([c.path.last])) == hopKey, |
| final modeRaw = firmwareBytes.length >= 82 | ||
| ? (firmwareBytes[81] & 0x3F) | ||
| : 0; | ||
| final mode = modeRaw.clamp(0, 3); | ||
| _pathHashByteWidth = mode + 1; // 1, 2, 3, or 4 bytes per hop |
| | Width | Max Unique Nodes | Typical Use | | ||
| |-------|-----------------|-------------| | ||
| | 1 byte | 256 | Single-byte node IDs | | ||
| | 2 bytes | 65,536 | Medium meshes | | ||
| | 3 bytes | 16.7M | Large networks | |
| final pathBytesList = <int>[]; | ||
| final invalidPrefixes = <String>[]; | ||
| final hexCharsPerHop = widget.pathHashByteWidth.clamp(1, 8) * 2; | ||
|
|
||
| for (final id in pathIds) { |
| AppLocalizations get _l10n => lookupAppLocalizations(_locale); | ||
|
|
||
| String _logSafe(String value) { | ||
| return Uri.encodeComponent(value.replaceAll('\n', ' ')); |
| Paths in messages and storage consist of: | ||
|
|
||
| - **`pathLength`**: Hop count reported in the radio header (may differ from decoded path) | ||
| - **`pathBytes`**: Raw bytes of the path, grouped by `pathHashByteWidth` | ||
| - **Example**: With `pathHashByteWidth=2`, a 3-hop path needs 6 bytes: |
| previousPosition = resolvedPosition; | ||
| } | ||
|
|
||
| // If the best candidate is much farther than the previous hop, it's likely not the correct match. |
There was a problem hiding this comment.
I wanted to ask if you could remove this filter, because introduces a lot of problems and you are editing this part already?
The closest candidate gets thrown away if next hop is further than 50km away and then it can have a cascading effect. Whereas this scenario is possible, for example I have the repeater "Sonnenberg AG/BL" with direct contact to "Transalp N1" 110km away one direction, and from there i could have about the same amount of distance, cumulating to over 200km.
This PR adds support for variable-length multi-byte routing paths in MeshCore Open.


The implementation is inspired by the meshcore-py project.