Skip to content

Commit 02580a6

Browse files
authored
Remove redundant map, simplify code; Show status and allow mouse click to peer to connect (#21)
* Remove redundant map, simplify code * more cleanup, drop addr entirely for now * Mouse click to connect * Click inside box check and better doc/comments
1 parent 2e4274a commit 02580a6

File tree

3 files changed

+77
-50
lines changed

3 files changed

+77
-50
lines changed

main.go

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,19 @@ var alignment = []table.Alignment{
5858
}
5959

6060
func PeerLine(idx int, peer tsnet.Peer, peerData tsnet.PeerData) []string {
61+
idxStr := strconv.Itoa(idx)
62+
switch peerData.Status {
63+
case tsnet.NotLinked:
64+
// leave uncolored
65+
case tsnet.Connecting:
66+
idxStr = tcolor.Inverse + Color16(tcolor.BrightYellow, idxStr)
67+
case tsnet.Failed:
68+
idxStr = tcolor.Inverse + Color16(tcolor.BrightRed, idxStr)
69+
case tsnet.Connected:
70+
idxStr = tcolor.Inverse + Color16(tcolor.BrightGreen, idxStr)
71+
}
6172
return []string{
62-
strconv.Itoa(idx),
73+
idxStr,
6374
Color16(tcolor.BrightCyan, peer.Name),
6475
Color16(tcolor.BrightGreen, peer.IP),
6576
Color16f(tcolor.Blue, "%d", peerData.Port),
@@ -91,6 +102,30 @@ func DarkGray(s string) string {
91102
return Color16(tcolor.DarkGray, s)
92103
}
93104

105+
func InitiatePeerConnection(srv *tsnet.Server, peer tsnet.Peer, peerData tsnet.PeerData) {
106+
log.Infof("Initiating connection to peer %q at %s:%d", peer.Name, peer.IP, peerData.Port)
107+
if connErr := srv.ConnectToPeer(peer); connErr != nil {
108+
log.Errf("Failed to connect to peer %s: %v", peer.Name, connErr)
109+
}
110+
}
111+
112+
// MouseInsideBox returns whether the mouse is inside the box and the index of the line inside the box.
113+
func MouseInsideBox(ap *ansipixels.AnsiPixels, tableWidth, numPeers int) (int, bool) {
114+
tableWidth -= 2 // remove the borders
115+
startTable := (ap.W-tableWidth)/2 + 1 // mouse coordinates start at 1
116+
endTable := startTable + tableWidth
117+
log.LogVf("MouseInsideBox: ap.Mx=%d, ap.My=%d, tableWidth=%d, startTable=%d, endTable=%d, numPeers=%d",
118+
ap.Mx, ap.My, tableWidth, startTable, endTable, numPeers)
119+
if ap.Mx < startTable || ap.Mx >= endTable {
120+
return -1, false
121+
}
122+
line := ap.My - 4 // accounts for border, our line and header and mouse coordinates starting at 1
123+
if line >= 0 && line < numPeers {
124+
return line, true
125+
}
126+
return -1, false
127+
}
128+
94129
func Main() int {
95130
fName := flag.String("name", "", "Name to use for this machine instead of the hostname")
96131
// echo -n "ts" | od -d -> 29556
@@ -105,7 +140,11 @@ func Main() int {
105140
if err := ap.Open(); err != nil {
106141
return 1 // error already logged
107142
}
108-
defer ap.Restore()
143+
ap.MouseClickOn()
144+
defer func() {
145+
ap.MouseClickOff()
146+
ap.Restore()
147+
}()
109148
id, err := LoadIdentity()
110149
if err != nil {
111150
return log.FErrf("Failed to load or create identity: %v", err)
@@ -147,6 +186,19 @@ func Main() int {
147186
return nil
148187
}
149188
var peersSnapshot []smap.KV[tsnet.Peer, tsnet.PeerData]
189+
tableWidth := 0
190+
ap.OnMouse = func() {
191+
if !ap.LeftClick() || !ap.MouseRelease() {
192+
return
193+
}
194+
if peerLine, ok := MouseInsideBox(ap, tableWidth, len(peersSnapshot)); ok {
195+
peer := peersSnapshot[peerLine]
196+
log.Infof("Left click (release) at %d,%d -> line %d - connecting to %q", ap.Mx, ap.My, peerLine+1, peer.Key.Name)
197+
InitiatePeerConnection(srv, peer.Key, peer.Value)
198+
} else {
199+
log.Infof("Left click (release) at %d,%d -> outside peer list", ap.Mx, ap.My)
200+
}
201+
}
150202
err = ap.FPSTicks(func() bool {
151203
// Only refresh if we had (log) output or something changed, so cursor blinks (!).
152204
logHadOutput := ap.FlushLogger()
@@ -169,7 +221,7 @@ func Main() int {
169221
lines = append(lines, PeerLine(idx, kv.Key, kv.Value))
170222
idx++
171223
}
172-
table.WriteTable(ap, 0, alignment, 1, lines, table.BorderOuterColumns)
224+
tableWidth = table.WriteTable(ap, 0, alignment, 1, lines, table.BorderOuterColumns)
173225
ap.RestoreCursorPos()
174226
ap.EndSyncMode()
175227
}
@@ -183,10 +235,7 @@ func Main() int {
183235
maxPeerIdx := len(peersSnapshot)
184236
if connectToPeerIdx <= maxPeerIdx {
185237
peer := peersSnapshot[connectToPeerIdx-1]
186-
log.Infof("Initiating connection to peer %q at %s:%d", peer.Key.Name, peer.Key.IP, peer.Value.Port)
187-
if connErr := srv.ConnectToPeer(peer.Key); connErr != nil {
188-
log.Errf("Failed to connect to peer %s: %v", peer.Key.Name, connErr)
189-
}
238+
InitiatePeerConnection(srv, peer.Key, peer.Value)
190239
} else {
191240
log.Warnf("No peer with index %d to connect to (max %d).", connectToPeerIdx, maxPeerIdx)
192241
}

tsnet/tsnet.go

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,16 @@ type Config struct {
5151
type ConnectionStatus int
5252

5353
const (
54-
Connecting ConnectionStatus = iota + 1
55-
ConnSent
54+
// NotLinked is the initial state once discovered.
55+
NotLinked ConnectionStatus = iota
56+
// Connecting is the state when a connection is being established.
57+
Connecting
58+
// Connected is the state when a connection has been established.
5659
Connected
57-
Disconnected
60+
// Failed is the state when a connection has failed.
5861
Failed
5962
)
6063

61-
type Connection struct {
62-
Peer Peer
63-
Status ConnectionStatus
64-
CreatedAt time.Time
65-
Addr *net.UDPAddr // Peer's unicast address
66-
}
67-
6864
type Server struct {
6965
// Our copy of the input config.
7066
Config
@@ -77,8 +73,7 @@ type Server struct {
7773
wg sync.WaitGroup
7874
Peers *smap.Map[Peer, PeerData]
7975
idStr string
80-
epoch atomic.Int32 // set to negative when stopped, panics after 2B ticks/if it wraps.
81-
connections *smap.Map[Peer, Connection] // peer -> Connection
76+
epoch atomic.Int32 // set to negative when stopped, panics after 2B ticks/if it wraps.
8277
}
8378

8479
type Peer struct {
@@ -92,14 +87,11 @@ type PeerData struct {
9287
Port int
9388
Epoch int32
9489
LastSeen time.Time
90+
Status ConnectionStatus
9591
}
9692

9793
func (c *Config) NewServer() *Server {
98-
return &Server{
99-
Config: *c,
100-
Peers: smap.New[Peer, PeerData](),
101-
connections: smap.New[Peer, Connection](),
102-
}
94+
return &Server{Config: *c, Peers: smap.New[Peer, PeerData]()}
10395
}
10496

10597
func (s *Server) Start(ctx context.Context) error {
@@ -236,11 +228,6 @@ func (s *Server) OurAddress() *net.UDPAddr {
236228
return s.ourSendAddr
237229
}
238230

239-
// Connections returns the connections map for testing/inspection.
240-
func (s *Server) Connections() *smap.Map[Peer, Connection] {
241-
return s.connections
242-
}
243-
244231
func (s *Server) change(version uint64) {
245232
if s.OnChange != nil {
246233
s.OnChange(version)
@@ -323,11 +310,14 @@ func (s *Server) runMulticastReceive(ctx context.Context) {
323310
}
324311
if v, ok := s.Peers.Get(peer); ok {
325312
log.S(log.Verbose, "Already known peer", log.Any("Peer", peer), log.Any("OldData", v), log.Any("NewData", data))
326-
// transfer the human hash (same pub key so same human hash)
313+
// Transfer the human hash (same pub key so same human hash)
327314
data.HumanHash = v.HumanHash
315+
// as well as the status
316+
data.Status = v.Status
328317
// Check if this is an updated port
329318
if v.Port != data.Port {
330319
log.Infof("Peer %q port changed from %d to %d", peer, v.Port, data.Port)
320+
data.Status = NotLinked
331321
}
332322
// Update last seen and epoch
333323
s.change(s.Peers.Set(peer, data))
@@ -451,35 +441,23 @@ func (s *Server) ConnectToPeer(peer Peer) error {
451441
// Get peer's address from discovery data
452442
peerData, exists := s.Peers.Get(peer)
453443
if !exists {
454-
return fmt.Errorf("peer %v not found in peer list", peer)
444+
return fmt.Errorf("peer %v not found (anymore) in peer list", peer)
455445
}
456-
457446
directPeerAddr := &net.UDPAddr{
458447
IP: net.ParseIP(peer.IP),
459448
Port: peerData.Port, // use the same port as discovery
460449
}
461-
462-
// Create connection entry
463-
conn := Connection{
464-
Peer: peer,
465-
Status: Connecting,
466-
CreatedAt: time.Now(),
467-
Addr: directPeerAddr,
468-
}
469-
s.connections.Set(peer, conn)
470-
471450
// Send connection request using shared socket
472451
message := fmt.Sprintf(ConnectMessageFormat, s.Name, peer.Name)
473452
_, err := s.dualUDPSock.WriteToUDP([]byte(message), directPeerAddr)
474453
if err != nil {
475-
s.connections.Delete(peer)
454+
peerData.Status = Failed
455+
s.Peers.Set(peer, peerData)
476456
return err
477457
}
478-
479-
// Update status to sent
480-
conn.Status = ConnSent
481-
s.connections.Set(peer, conn)
482-
458+
// Update status to sent = connecting
459+
peerData.Status = Connecting
460+
s.Peers.Set(peer, peerData)
483461
log.Infof("Connection request sent to %s (%s)", peer.Name, peer.IP)
484462
return nil
485463
}

tsnet/tsnet_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ func TestPeerDiscovery(t *testing.T) {
134134
time.Sleep(200 * time.Millisecond)
135135

136136
// Check that the connection was created on A's side
137-
connA, exists := serverA.Connections().Get(peerB)
138-
if !exists {
137+
connA, exists := serverA.Peers.Get(peerB)
138+
if !exists || connA.Status != tsnet.Connecting {
139139
t.Fatal("Connection from A to B not found in A's connection map")
140140
}
141141
t.Logf("✓ Connection created on A's side: status %v", connA.Status)

0 commit comments

Comments
 (0)