misc docker changes, fix typos, fix postoffice tests

This commit is contained in:
Reese Norris
2025-05-18 20:02:59 -07:00
parent 75178b7557
commit 35dfc7d446
6 changed files with 81 additions and 75 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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} {

View File

@@ -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 {