mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 00:45:21 +00:00
Make peer discovery instant and fix connection stability
- Removed 8-character filter that was blocking temp IDs during transition - Added immediate peer list updates for initial connections (no debounce) - Reduced debounce interval to 100ms for subsequent updates - Fixed race condition in peripheral ID mapping by adding before removing - Removed aggressive peer cleanup in unsubscribe handler - Only remove real peer IDs from activePeers on disconnect, not temp IDs - Added better debug logging for peer add/remove operations - Fixed peripheral lookup to handle mapping transitions Peer discovery is now instant when devices connect, and connections are more stable.
This commit is contained in:
@@ -83,7 +83,7 @@ class BluetoothMeshService: NSObject {
|
|||||||
|
|
||||||
// Peer list update debouncing
|
// Peer list update debouncing
|
||||||
private var peerListUpdateTimer: Timer?
|
private var peerListUpdateTimer: Timer?
|
||||||
private let peerListUpdateDebounceInterval: TimeInterval = 0.5 // 500ms debounce
|
private let peerListUpdateDebounceInterval: TimeInterval = 0.1 // 100ms debounce for more responsive updates
|
||||||
|
|
||||||
// Cover traffic for privacy
|
// Cover traffic for privacy
|
||||||
private var coverTrafficTimer: Timer?
|
private var coverTrafficTimer: Timer?
|
||||||
@@ -782,8 +782,7 @@ class BluetoothMeshService: NSObject {
|
|||||||
// Ensure peerID is valid
|
// Ensure peerID is valid
|
||||||
return !peerID.isEmpty &&
|
return !peerID.isEmpty &&
|
||||||
peerID != "unknown" &&
|
peerID != "unknown" &&
|
||||||
peerID != myPeerID &&
|
peerID != myPeerID
|
||||||
peerID.count <= 8 // Filter out temp IDs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print("[DEBUG] Active peers: \(peersCopy), Valid peers: \(validPeers)")
|
print("[DEBUG] Active peers: \(peersCopy), Valid peers: \(validPeers)")
|
||||||
@@ -791,20 +790,30 @@ class BluetoothMeshService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debounced peer list update notification
|
// Debounced peer list update notification
|
||||||
private func notifyPeerListUpdate() {
|
private func notifyPeerListUpdate(immediate: Bool = false) {
|
||||||
// Cancel any pending update
|
if immediate {
|
||||||
peerListUpdateTimer?.invalidate()
|
// For initial connections, update immediately
|
||||||
|
|
||||||
// Schedule a new update after debounce interval
|
|
||||||
peerListUpdateTimer = Timer.scheduledTimer(withTimeInterval: peerListUpdateDebounceInterval, repeats: false) { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
let connectedPeerIDs = self.getAllConnectedPeerIDs()
|
let connectedPeerIDs = self.getAllConnectedPeerIDs()
|
||||||
print("[DEBUG] Notifying peer list update after debounce: \(connectedPeerIDs.count) peers")
|
print("[DEBUG] Notifying peer list update immediately: \(connectedPeerIDs.count) peers")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.delegate?.didUpdatePeerList(connectedPeerIDs)
|
self.delegate?.didUpdatePeerList(connectedPeerIDs)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Cancel any pending update
|
||||||
|
peerListUpdateTimer?.invalidate()
|
||||||
|
|
||||||
|
// Schedule a new update after debounce interval
|
||||||
|
peerListUpdateTimer = Timer.scheduledTimer(withTimeInterval: peerListUpdateDebounceInterval, repeats: false) { [weak self] _ in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
let connectedPeerIDs = self.getAllConnectedPeerIDs()
|
||||||
|
print("[DEBUG] Notifying peer list update after debounce: \(connectedPeerIDs.count) peers")
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.delegate?.didUpdatePeerList(connectedPeerIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1364,10 +1373,11 @@ class BluetoothMeshService: NSObject {
|
|||||||
// Find if this peripheral is currently mapped with a temp ID
|
// Find if this peripheral is currently mapped with a temp ID
|
||||||
if let tempID = self.connectedPeripherals.first(where: { $0.value == peripheral })?.key,
|
if let tempID = self.connectedPeripherals.first(where: { $0.value == peripheral })?.key,
|
||||||
tempID.count > 8 { // It's a temp ID
|
tempID.count > 8 { // It's a temp ID
|
||||||
// Remove temp mapping and add real peer ID mapping
|
// Add real peer ID mapping BEFORE removing temp mapping
|
||||||
self.connectedPeripherals.removeValue(forKey: tempID)
|
|
||||||
self.connectedPeripherals[senderID] = peripheral
|
self.connectedPeripherals[senderID] = peripheral
|
||||||
// Updated peripheral mapping
|
// Then remove temp mapping
|
||||||
|
self.connectedPeripherals.removeValue(forKey: tempID)
|
||||||
|
print("[DEBUG] Updated peripheral mapping from \(tempID) to \(senderID)")
|
||||||
|
|
||||||
// Transfer RSSI from temp ID to peer ID
|
// Transfer RSSI from temp ID to peer ID
|
||||||
if let rssi = self.peripheralRSSI[tempID] {
|
if let rssi = self.peripheralRSSI[tempID] {
|
||||||
@@ -1375,6 +1385,11 @@ class BluetoothMeshService: NSObject {
|
|||||||
self.peripheralRSSI.removeValue(forKey: tempID)
|
self.peripheralRSSI.removeValue(forKey: tempID)
|
||||||
// Transferred RSSI to peer
|
// Transferred RSSI to peer
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Peripheral already has correct mapping or doesn't exist
|
||||||
|
if !self.connectedPeripherals.keys.contains(senderID) {
|
||||||
|
self.connectedPeripherals[senderID] = peripheral
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1389,7 +1404,7 @@ class BluetoothMeshService: NSObject {
|
|||||||
|
|
||||||
// Only notify if this was actually a new peer
|
// Only notify if this was actually a new peer
|
||||||
if wasNewPeer {
|
if wasNewPeer {
|
||||||
self.notifyPeerListUpdate()
|
self.notifyPeerListUpdate(immediate: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1445,7 +1460,7 @@ class BluetoothMeshService: NSObject {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.delegate?.didConnectToPeer(nickname)
|
self.delegate?.didConnectToPeer(nickname)
|
||||||
}
|
}
|
||||||
self.notifyPeerListUpdate()
|
self.notifyPeerListUpdate(immediate: true)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// Check if this is a favorite peer and send notification
|
// Check if this is a favorite peer and send notification
|
||||||
@@ -1841,17 +1856,37 @@ extension BluetoothMeshService: CBCentralManagerDelegate {
|
|||||||
connectionBackoff.removeValue(forKey: peripheralID)
|
connectionBackoff.removeValue(forKey: peripheralID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let peerID = connectedPeripherals.first(where: { $0.value == peripheral })?.key {
|
// Find peer ID for this peripheral (could be temp ID or real ID)
|
||||||
|
var foundPeerID: String? = nil
|
||||||
|
for (id, per) in connectedPeripherals {
|
||||||
|
if per == peripheral {
|
||||||
|
foundPeerID = id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let peerID = foundPeerID {
|
||||||
connectedPeripherals.removeValue(forKey: peerID)
|
connectedPeripherals.removeValue(forKey: peerID)
|
||||||
peripheralCharacteristics.removeValue(forKey: peripheral)
|
peripheralCharacteristics.removeValue(forKey: peripheral)
|
||||||
|
|
||||||
// Remove from active peers with proper locking
|
print("[DEBUG] Peripheral disconnected with ID: \(peerID)")
|
||||||
activePeersLock.lock()
|
|
||||||
activePeers.remove(peerID)
|
|
||||||
activePeersLock.unlock()
|
|
||||||
|
|
||||||
announcedPeers.remove(peerID)
|
// Only remove from active peers if it's not a temp ID
|
||||||
announcedToPeers.remove(peerID)
|
// Temp IDs shouldn't be in activePeers anyway
|
||||||
|
if peerID.count <= 8 { // Real peer ID
|
||||||
|
activePeersLock.lock()
|
||||||
|
let wasRemoved = activePeers.remove(peerID) != nil
|
||||||
|
activePeersLock.unlock()
|
||||||
|
|
||||||
|
if wasRemoved {
|
||||||
|
print("[DEBUG] Removed peer \(peerID) from active peers due to disconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
announcedPeers.remove(peerID)
|
||||||
|
announcedToPeers.remove(peerID)
|
||||||
|
} else {
|
||||||
|
print("[DEBUG] Peripheral with temp ID \(peerID) disconnected, not removing from active peers")
|
||||||
|
}
|
||||||
|
|
||||||
// Clear cached messages tracking for this peer to allow re-sending if they reconnect
|
// Clear cached messages tracking for this peer to allow re-sending if they reconnect
|
||||||
cachedMessagesSentToPeer.remove(peerID)
|
cachedMessagesSentToPeer.remove(peerID)
|
||||||
@@ -2139,31 +2174,9 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
|
|||||||
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
|
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
|
||||||
subscribedCentrals.removeAll { $0 == central }
|
subscribedCentrals.removeAll { $0 == central }
|
||||||
|
|
||||||
// If no more centrals are subscribed, clear all central-connected peers
|
// Don't aggressively remove peers when centrals unsubscribe
|
||||||
if subscribedCentrals.isEmpty {
|
// This was causing issues where valid peers were being removed during connection setup
|
||||||
// Find and remove peers that were connected as centrals only
|
// TODO: Track which peers are connected through which central for proper cleanup
|
||||||
activePeersLock.lock()
|
|
||||||
let peersToRemove = activePeers.filter { peerID in
|
|
||||||
!connectedPeripherals.keys.contains(peerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
for peerID in peersToRemove {
|
|
||||||
activePeers.remove(peerID)
|
|
||||||
announcedToPeers.remove(peerID)
|
|
||||||
peerNicknamesLock.lock()
|
|
||||||
let nickname = peerNicknames[peerID]
|
|
||||||
peerNicknamesLock.unlock()
|
|
||||||
|
|
||||||
if let nickname = nickname {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.delegate?.didDisconnectFromPeer(nickname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activePeersLock.unlock()
|
|
||||||
|
|
||||||
self.notifyPeerListUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure advertising continues for reconnection
|
// Ensure advertising continues for reconnection
|
||||||
if peripheralManager.state == .poweredOn && !peripheralManager.isAdvertising {
|
if peripheralManager.state == .poweredOn && !peripheralManager.isAdvertising {
|
||||||
|
|||||||
Reference in New Issue
Block a user