diff --git a/docker-compose.yml b/docker-compose.yml index 38dc91e..7b85634 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: expose: - "13618/tcp" # Internal HTTP REST API service. The webserver talks to this in order to obtain FSD state info. ports: - - "6809/tcp" + - "6809:6809/tcp" environment: DATABASE_SOURCE_NAME: /db/openfsd.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL) DATABASE_AUTO_MIGRATE: true @@ -23,8 +23,8 @@ services: restart: unless-stopped container_name: openfsd_web hostname: openfsd_web - expose: - - "8000/tcp" + ports: + - "8000:8000/tcp" environment: DATABASE_SOURCE_NAME: /db/openfsd.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL) FSD_HTTP_SERVICE_ADDRESS: "http://openfsd_fsd:13618" diff --git a/fsd/client.go b/fsd/client.go index 1cbce7f..2ab828e 100644 --- a/fsd/client.go +++ b/fsd/client.go @@ -21,14 +21,14 @@ type Client struct { flightPlan atomic.String assignedBeaconCode atomic.String - frequency atomic.String // OnlineUserATC frequency + frequency atomic.String // ATC frequency altitude atomic.Int32 // OnlineUserPilot altitude groundspeed atomic.Int32 // OnlineUserPilot ground speed transponder atomic.String // Active pilot transponder heading atomic.Int32 // OnlineUserPilot heading lastUpdated atomic.Time // Last updated time - facilityType int // OnlineUserATC facility type. This value is only relevant for OnlineUserATC + facilityType int // ATC facility type. This value is only relevant for ATC loginData authState vatsimAuthState diff --git a/fsd/conn.go b/fsd/conn.go index 7718061..a442ab1 100644 --- a/fsd/conn.go +++ b/fsd/conn.go @@ -97,14 +97,14 @@ func sendServerIdent(conn io.Writer) (err error) { // loginData holds the data extracted from the Client's login packets. type loginData struct { clientChallenge string // Optional Client challenge for authentication - callsign string // Callsign of the Client (OnlineUserATC or pilot) + callsign string // Callsign of the Client cid int // Cert ID realName string // Real name networkRating NetworkRating // Network rating of the Client protoRevision int // Protocol revision loginTime time.Time // Time of login clientId uint16 // Client ID - isAtc bool // True if the Client is an OnlineUserATC, false if a pilot + isAtc bool // True if the Client is an ATC, false if a pilot } // ErrInvalidAddPacket is returned when the add packet from the Client is invalid. @@ -173,7 +173,7 @@ func readLoginPackets(conn net.Conn, scanner *bufio.Scanner) (data loginData, to if data.isAtc { if countFields(addPacket) != 7 { err = ErrInvalidAddPacket - sendError(conn, SyntaxError, "Invalid number of fields in OnlineUserATC add packet") + sendError(conn, SyntaxError, "Invalid number of fields in ATC add packet") return } } else { @@ -196,7 +196,7 @@ func readLoginPackets(conn net.Conn, scanner *bufio.Scanner) (data loginData, to data.realName = string(getField(addPacket, 2)) if data.cid, err = strconv.Atoi(string(getField(addPacket, 3))); err != nil { err = ErrInvalidAddPacket - sendError(conn, SyntaxError, "Invalid CID in OnlineUserATC add packet") + sendError(conn, SyntaxError, "Invalid CID in ATC add packet") return } token = string(getField(addPacket, 4)) @@ -209,7 +209,7 @@ func readLoginPackets(conn net.Conn, scanner *bufio.Scanner) (data loginData, to data.networkRating = NetworkRating(networkRating) if data.protoRevision, err = strconv.Atoi(string(getField(addPacket, 6))); err != nil { err = ErrInvalidAddPacket - sendError(conn, SyntaxError, "Invalid protocol revision in OnlineUserATC add packet") + sendError(conn, SyntaxError, "Invalid protocol revision in ATC add packet") return } } else { diff --git a/fsd/handler.go b/fsd/handler.go index 195a2fb..5fc55ba 100644 --- a/fsd/handler.go +++ b/fsd/handler.go @@ -52,7 +52,7 @@ func (s *Server) emptyHandler(client *Client, packet []byte) { func (s *Server) handleTextMessage(client *Client, packet []byte) { recipient := getField(packet, 1) - // OnlineUserATC chat + // ATC chat if string(recipient) == "@49999" { if !client.isAtc { return @@ -167,7 +167,7 @@ func (s *Server) handleFastPilotPosition(client *Client, packet []byte) { broadcastRangedVelocity(s.postOffice, client, packet) } -// handleDelete handles logic for Delete OnlineUserATC `#DA` and Delete OnlineUserPilot `#DP` packets +// handleDelete handles logic for Delete ATC `#DA` and Delete OnlineUserPilot `#DP` packets func (s *Server) handleDelete(client *Client, packet []byte) { // Broadcast delete packet broadcastAll(s.postOffice, client, packet) @@ -185,7 +185,7 @@ func (s *Server) handleSquawkbox(client *Client, packet []byte) { // handleProcontroller handles logic for Pro Controller `#PC` packets func (s *Server) handleProcontroller(client *Client, packet []byte) { - // OnlineUserATC-only packet + // ATC-only packet if !client.isAtc { return } @@ -224,7 +224,7 @@ func (s *Server) handleProcontroller(client *Client, packet []byte) { "DP", // Push to departure list "ST": // Set flight strip - // Only active OnlineUserATC above OBS + // Only active ATC above OBS if client.facilityType <= 0 { client.sendError(InvalidControlError, "Invalid control") return @@ -244,7 +244,7 @@ func (s *Server) handleClientQuery(client *Client, packet []byte) { // Handle queries sent to SERVER if string(recipient) == "SERVER" { switch string(queryType) { - case "OnlineUserATC": + case "ATC": s.handleClientQueryATCRequest(client, packet) case "IP": s.handleClientQueryIPRequest(client, packet) @@ -258,7 +258,7 @@ func (s *Server) handleClientQuery(client *Client, packet []byte) { if bytes.HasPrefix(recipient, []byte("@")) { switch string(queryType) { - // Unprivileged OnlineUserATC queries + // Unprivileged ATC queries case "BY", // Request relief "HI", // Cancel request relief @@ -268,14 +268,14 @@ func (s *Server) handleClientQuery(client *Client, packet []byte) { "NEWATIS", // Broadcast new ATIS letter "NEWINFO": // Broadcast new ATIS info - // OnlineUserATC only + // ATC only if !client.isAtc { client.sendError(InvalidControlError, "Invalid control") return } broadcastRangedAtcOnly(s.postOffice, client, packet) - // Privileged OnlineUserATC queries + // Privileged ATC queries case "IT", // Initiate track "DR", // Drop track @@ -288,7 +288,7 @@ func (s *Server) handleClientQuery(client *Client, packet []byte) { "EST", // Set estimate time "GD": // Set global data - // OnlineUserATC above OBS facility only + // ATC above OBS facility only if !client.isAtc || client.facilityType <= 0 { client.sendError(InvalidControlError, "Invalid control") return @@ -336,7 +336,7 @@ func (s *Server) handleClientQuery(client *Client, packet []byte) { func (s *Server) handleClientQueryATCRequest(client *Client, packet []byte) { if countFields(packet) != 4 { - client.sendError(SyntaxError, "Invalid OnlineUserATC request") + client.sendError(SyntaxError, "Invalid ATC request") return } @@ -349,9 +349,9 @@ func (s *Server) handleClientQueryATCRequest(client *Client, packet []byte) { var p string if targetClient.facilityType > 0 { - p = fmt.Sprintf("$CRSERVER:%s:OnlineUserATC:Y:%s\r\n", client.callsign, targetCallsign) + p = fmt.Sprintf("$CRSERVER:%s:ATC:Y:%s\r\n", client.callsign, targetCallsign) } else { - p = fmt.Sprintf("$CRSERVER:%s:OnlineUserATC:N:%s\r\n", client.callsign, targetCallsign) + p = fmt.Sprintf("$CRSERVER:%s:ATC:N:%s\r\n", client.callsign, targetCallsign) } client.send(p) } @@ -451,7 +451,7 @@ func (s *Server) handleAuthChallenge(client *Client, packet []byte) { } func (s *Server) handleHandoff(client *Client, packet []byte) { - // Active >OBS OnlineUserATC only + // Active >OBS ATC only if !client.isAtc || client.facilityType <= 1 { return } diff --git a/fsd/postoffice_test.go b/fsd/postoffice_test.go index cce916a..f88a177 100644 --- a/fsd/postoffice_test.go +++ b/fsd/postoffice_test.go @@ -9,9 +9,13 @@ import ( "testing" ) +// TestRegister tests the registration of clients with unique and duplicate callsigns. func TestRegister(t *testing.T) { p := newPostOffice() - client1 := &Client{loginData: loginData{callsign: "client1"}, latLon: [2]float64{0, 0}, visRange: 100000} + client1 := &Client{loginData: loginData{callsign: "client1"}} + client1.lat.Store(0) + client1.lon.Store(0) + client1.visRange.Store(100000) err := p.register(client1) if err != nil { t.Errorf("expected no error, got %v", err) @@ -19,7 +23,10 @@ func TestRegister(t *testing.T) { if p.clientMap["client1"] != client1 { t.Errorf("expected client1 in map") } - client2 := &Client{loginData: loginData{callsign: "client1"}, latLon: [2]float64{0, 0}, visRange: 100000} + client2 := &Client{loginData: loginData{callsign: "client1"}} + client2.lat.Store(0) + client2.lon.Store(0) + client2.visRange.Store(100000) err = p.register(client2) if err != ErrCallsignInUse { t.Errorf("expected ErrCallsignInUse, got %v", err) @@ -29,22 +36,21 @@ func TestRegister(t *testing.T) { } } +// TestRelease tests the removal of a client and its effect on search results. func TestRelease(t *testing.T) { p := newPostOffice() - client1 := &Client{ - loginData: loginData{callsign: "client1"}, - latLon: [2]float64{0, 0}, - visRange: 100000, - } + client1 := &Client{loginData: loginData{callsign: "client1"}} + client1.lat.Store(0) + client1.lon.Store(0) + client1.visRange.Store(100000) err := p.register(client1) if err != nil { t.Fatal(err) } - client2 := &Client{ - loginData: loginData{callsign: "client2"}, - latLon: [2]float64{0, 0}, - visRange: 200000, - } + client2 := &Client{loginData: loginData{callsign: "client2"}} + client2.lat.Store(0) + client2.lon.Store(0) + client2.visRange.Store(200000) err = p.register(client2) if err != nil { t.Fatal(err) @@ -75,22 +81,21 @@ func TestRelease(t *testing.T) { } } +// TestUpdatePosition tests updating a client's position and its effect on search. func TestUpdatePosition(t *testing.T) { p := newPostOffice() - client1 := &Client{ - loginData: loginData{callsign: "client1"}, - latLon: [2]float64{0, 0}, - visRange: 100000, - } + client1 := &Client{loginData: loginData{callsign: "client1"}} + client1.lat.Store(0) + client1.lon.Store(0) + client1.visRange.Store(100000) err := p.register(client1) if err != nil { t.Fatal(err) } - client2 := &Client{ - loginData: loginData{callsign: "client2"}, - latLon: [2]float64{0.5, 0.5}, - visRange: 100000, - } + client2 := &Client{loginData: loginData{callsign: "client2"}} + client2.lat.Store(0.5) + client2.lon.Store(0.5) + client2.visRange.Store(100000) err = p.register(client2) if err != nil { t.Fatal(err) @@ -105,9 +110,11 @@ func TestUpdatePosition(t *testing.T) { t.Errorf("expected to find client2, got %v", found) } - newLatLon := [2]float64{100, 100} + // Assuming updatePosition now takes lat, lon, visRange separately + newLat := 100.0 + newLon := 100.0 newVisRange := 100000.0 - p.updatePosition(client2, newLatLon, newVisRange) + p.updatePosition(client2, [2]float64{newLat, newLon}, newVisRange) found = nil p.search(client1, func(recipient *Client) bool { @@ -119,31 +126,29 @@ func TestUpdatePosition(t *testing.T) { } } +// TestSearch tests the search functionality with multiple clients. func TestSearch(t *testing.T) { p := newPostOffice() - client1 := &Client{ - loginData: loginData{callsign: "client1"}, - latLon: [2]float64{32.0, -117.0}, - visRange: 100000, - } + client1 := &Client{loginData: loginData{callsign: "client1"}} + client1.lat.Store(32.0) + client1.lon.Store(-117.0) + client1.visRange.Store(100000) err := p.register(client1) if err != nil { t.Fatal(err) } - client2 := &Client{ - loginData: loginData{callsign: "client2"}, - latLon: [2]float64{33.0, -117.0}, - visRange: 50000, - } + client2 := &Client{loginData: loginData{callsign: "client2"}} + client2.lat.Store(33.0) + client2.lon.Store(-117.0) + client2.visRange.Store(50000) err = p.register(client2) if err != nil { t.Fatal(err) } - client3 := &Client{ - loginData: loginData{callsign: "client3"}, - latLon: [2]float64{34.0, -117.0}, - visRange: 50000, - } + client3 := &Client{loginData: loginData{callsign: "client3"}} + client3.lat.Store(34.0) + client3.lon.Store(-117.0) + client3.visRange.Store(50000) err = p.register(client3) if err != nil { t.Fatal(err) @@ -176,11 +181,10 @@ func TestSearch(t *testing.T) { t.Errorf("expected no clients found, got %v", found) } - client4 := &Client{ - loginData: loginData{callsign: "client4"}, - latLon: [2]float64{31.0, -117.0}, - visRange: 50000, - } + client4 := &Client{loginData: loginData{callsign: "client4"}} + client4.lat.Store(31.0) + client4.lon.Store(-117.0) + client4.visRange.Store(50000) err = p.register(client4) if err != nil { t.Fatal(err) @@ -209,6 +213,7 @@ func TestSearch(t *testing.T) { } } +// TestCalculateBoundingBox remains unchanged as it doesn't involve Client. func TestCalculateBoundingBox(t *testing.T) { const earthRadius = 6371000.0 tests := []struct { @@ -252,7 +257,7 @@ func approxEqual(a, b float64) bool { return math.Abs(a-b) < epsilon } -// BenchmarkDistance measures the performance of the distance function using pre-generated pseudo-random coordinates. +// BenchmarkDistance remains unchanged as it doesn't involve Client directly. func BenchmarkDistance(b *testing.B) { const numPairs = 1024 * 64 lats1 := make([]float64, numPairs) @@ -281,6 +286,7 @@ func BenchmarkDistance(b *testing.B) { } } +// benchmarkSearchWithN benchmarks search performance with n clients. func benchmarkSearchWithN(b *testing.B, n int) { // Create postOffice p := newPostOffice() @@ -288,11 +294,10 @@ func benchmarkSearchWithN(b *testing.B, n int) { // Create n clients clients := make([]*Client, n) for i := 0; i < n; i++ { - clients[i] = &Client{ - loginData: loginData{callsign: fmt.Sprintf("Client%d", i)}, - latLon: [2]float64{rand.Float64(), rand.Float64()}, - visRange: 10000, - } + clients[i] = &Client{loginData: loginData{callsign: fmt.Sprintf("Client%d", i)}} + clients[i].lat.Store(-90 + rand.Float64()*180) // Latitude: -90 to 90 + clients[i].lon.Store(-180 + rand.Float64()*360) // Longitude: -180 to 180 + clients[i].visRange.Store(10000) p.register(clients[i]) } @@ -314,6 +319,7 @@ func benchmarkSearchWithN(b *testing.B, n int) { } } +// BenchmarkSearch runs benchmarks for different client counts. func BenchmarkSearch(b *testing.B) { rand.Seed(42) for _, n := range []int{100, 1000, 10000} { diff --git a/fsd/util.go b/fsd/util.go index 76ca9a3..792283f 100644 --- a/fsd/util.go +++ b/fsd/util.go @@ -229,7 +229,7 @@ func broadcastRangedVelocity(po *postOffice, client *Client, packet []byte) { }) } -// broadcastRangedAtcOnly broadcasts a packet to all OnlineUserATC clients in range +// broadcastRangedAtcOnly broadcasts a packet to all ATC clients in range func broadcastRangedAtcOnly(po *postOffice, client *Client, packet []byte) { packetStr := string(packet) po.search(client, func(recipient *Client) bool { @@ -250,7 +250,7 @@ func broadcastAll(po *postOffice, client *Client, packet []byte) { }) } -// broadcastAllATC broadcasts a packet to all OnlineUserATC on entire server +// broadcastAllATC broadcasts a packet to all ATC on entire server func broadcastAllATC(po *postOffice, client *Client, packet []byte) { packetStr := string(packet) po.all(client, func(recipient *Client) bool {