mirror of
https://github.com/renorris/openfsd
synced 2026-03-22 06:25:35 +08:00
misc docker changes, fix typos, fix postoffice tests
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
10
fsd/conn.go
10
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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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} {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user