9 Commits

Author SHA1 Message Date
Reese Norris
e09ff9e64e rename atc to controllers in datafeed 2025-05-25 14:54:31 -07:00
Reese Norris
933edc0478 update datafeed to be more vatsim-compliant 2025-05-25 14:51:45 -07:00
Reese Norris
6e3a179a3a convert visual range unit for ATC datafeed to nautical miles 2025-05-25 14:29:30 -07:00
Reese Norris
f63d89eb3e only do velocity distance calculation for relevant clients 2025-05-25 14:27:07 -07:00
Reese Norris
bc7a37e490 properly store sendfast state 2025-05-25 14:19:08 -07:00
Reese Norris
7d17066289 properly initialize Client latlon 2025-05-25 14:00:14 -07:00
Reese Norris
2943735be6 add AUTOMATIC entry to server list API for client compatibility 2025-05-25 13:52:19 -07:00
Reese Norris
c0155e78d4 store client coords as a single atomic unit 2025-05-25 11:57:06 -07:00
Reese Norris
2f423ed824 update gitignore 2025-05-25 11:56:38 -07:00
8 changed files with 130 additions and 25 deletions

View File

@@ -3,4 +3,5 @@ docker-compose.yml
mkdocs.yml
README.md
docs
**tmp**
**tmp**
build-and-push.sh

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@
.vscode
*.db
**tmp**
build-and-push.sh

View File

@@ -16,7 +16,8 @@ type Client struct {
cancelCtx func()
sendChan chan string
lat, lon, visRange atomic.Float64
coords atomic.Value
visRange atomic.Float64
closestVelocityClientDistance float64 // The closest Velocity-compatible client in meters
flightPlan atomic.String
@@ -37,11 +38,12 @@ type Client struct {
}
type LatLon struct {
lat, lon float64
}
func newClient(ctx context.Context, conn net.Conn, scanner *bufio.Scanner, loginData loginData) (client *Client) {
clientCtx, cancel := context.WithCancel(ctx)
return &Client{
client = &Client{
conn: conn,
scanner: scanner,
ctx: clientCtx,
@@ -49,6 +51,8 @@ func newClient(ctx context.Context, conn net.Conn, scanner *bufio.Scanner, login
sendChan: make(chan string, 32),
loginData: loginData,
}
client.setLatLon(0, 0)
return
}
func (c *Client) senderWorker() {
@@ -125,5 +129,10 @@ func (s *Server) eventLoop(client *Client) {
}
func (c *Client) latLon() [2]float64 {
return [2]float64{c.lat.Load(), c.lon.Load()}
latLon := c.coords.Load().(LatLon)
return [2]float64{latLon.lat, latLon.lon}
}
func (c *Client) setLatLon(lat, lon float64) {
c.coords.Store(LatLon{lat: lat, lon: lon})
}

View File

@@ -165,13 +165,17 @@ func (s *Server) handlePilotPosition(client *Client, packet []byte) {
client.lastUpdated.Store(time.Now())
// Check if we need to update the sendfast state
if client.sendFastEnabled {
if (client.closestVelocityClientDistance / 1852.0) > 5.0 { // 5.0 nautical miles
sendDisableSendFastPacket(client)
}
} else {
if (client.closestVelocityClientDistance / 1852.0) < 5.0 { // 5.0 nautical miles
sendEnableSendFastPacket(client)
if client.protoRevision == 101 {
if client.sendFastEnabled {
if (client.closestVelocityClientDistance / 1852.0) > 5.0 { // 5.0 nautical miles
client.sendFastEnabled = false
sendDisableSendFastPacket(client)
}
} else {
if (client.closestVelocityClientDistance / 1852.0) < 5.0 { // 5.0 nautical miles
client.sendFastEnabled = true
sendEnableSendFastPacket(client)
}
}
}
}

View File

@@ -110,13 +110,14 @@ func (s *Server) handleGetOnlineUsers(c *gin.Context) {
}
for _, client := range clientMap {
latLon := client.latLon()
genData := OnlineUserGeneralData{
Callsign: client.callsign,
CID: client.cid,
Name: client.realName,
NetworkRating: int(client.networkRating),
Latitude: client.lat.Load(),
Longitude: client.lon.Load(),
Latitude: latLon[0],
Longitude: latLon[1],
LogonTime: client.loginTime,
LastUpdated: client.lastUpdated.Load(),
}
@@ -126,7 +127,7 @@ func (s *Server) handleGetOnlineUsers(c *gin.Context) {
OnlineUserGeneralData: genData,
Frequency: client.frequency.Load(),
Facility: client.facilityType,
VisRange: int(client.visRange.Load()),
VisRange: int(client.visRange.Load() * 0.000539957), // Convert meters to nautical miles
}
resData.ATC = append(resData.ATC, atc)
} else {

View File

@@ -68,8 +68,7 @@ func (p *postOffice) updatePosition(client *Client, newCenter [2]float64, newVis
oldMin, oldMax := calculateBoundingBox(client.latLon(), client.visRange.Load())
newMin, newMax := calculateBoundingBox(newCenter, newVisRange)
client.lat.Store(newCenter[0])
client.lon.Store(newCenter[1])
client.setLatLon(newCenter[0], newCenter[1])
client.visRange.Store(newVisRange)
// Avoid redundant updates
@@ -99,13 +98,15 @@ func (p *postOffice) search(client *Client, callback func(recipient *Client) boo
return true // Ignore self
}
if foundClient.protoRevision == 101 {
dist := distance(client.lat.Load(), client.lon.Load(), foundClient.lat.Load(), foundClient.lon.Load())
if !client.isAtc && client.protoRevision == 101 && foundClient.protoRevision == 101 {
clientLatLon := client.latLon()
foundClientLatLon := foundClient.latLon()
dist := distance(clientLatLon[0], clientLatLon[1], foundClientLatLon[0], foundClientLatLon[1])
if dist < client.closestVelocityClientDistance {
client.closestVelocityClientDistance = dist
}
}
return callback(foundClient)
})
p.treeLock.RUnlock()

View File

@@ -145,6 +145,15 @@ func (s *Server) handleGetServersJSON(c *gin.Context) {
ClientsConnectionAllowed: 99,
IsSweatbox: isSweatbox,
},
{
Ident: "AUTOMATIC",
HostnameOrIp: serverHostname,
Location: serverLocation,
Name: serverIdent,
ClientConnectionsAllowed: true,
ClientsConnectionAllowed: 99,
IsSweatbox: isSweatbox,
},
}
res, err := json.Marshal(&dataJson)
@@ -190,6 +199,15 @@ func (s *Server) generateServersTxt() (txt string, err error) {
ClientsConnectionAllowed: 99,
IsSweatbox: false,
},
{
Ident: "AUTOMATIC",
HostnameOrIp: serverHostname,
Location: serverLocation,
Name: serverIdent,
ClientConnectionsAllowed: true,
ClientsConnectionAllowed: 99,
IsSweatbox: false,
},
}
buf := bytes.Buffer{}
@@ -250,8 +268,49 @@ func (s *Server) getBaseURLOrErr(c *gin.Context) (baseURL string, ok bool) {
}
type Datafeed struct {
Pilots []fsd.OnlineUserPilot `json:"pilots"`
ATC []fsd.OnlineUserATC `json:"atc"`
General DatafeedGeneral `json:"general"`
Pilots []DatafeedPilot `json:"pilots"`
ATC []DatafeedATC `json:"controllers"`
}
type DatafeedGeneral struct {
Version int `json:"version"`
UpdateTimestamp time.Time `json:"update_timestamp"`
ConnectedClients int `json:"connected_clients"`
UniqueUsers int `json:"unique_users"`
}
type DatafeedPilot struct {
fsd.OnlineUserPilot
Server string `json:"server"`
PilotRating int `json:"pilot_rating"` // INOP placeholder
MilitaryRating int `json:"military_rating"` // INOP placeholder
QnhIHg float64 `json:"qnh_i_hg"` // INOP placeholder
QnhMb int `json:"qnh_mb"` // INOP placeholder
FlightPlan *DatafeedFlightplan `json:"flight_plan,omitempty"` // INOP placeholder
}
type DatafeedFlightplan struct {
FlightRules string `json:"flight_rules"`
Aircraft string `json:"aircraft"`
AircraftFAA string `json:"aircraft_faa"`
AircraftShort string `json:"aircraft_short"`
Departure string `json:"departure"`
Arrival string `json:"arrival"`
Alternate string `json:"alternate"`
DepTime string `json:"deptime"`
EnrouteTime string `json:"enroute_time"`
FuelTime string `json:"fuel_time"`
Remarks string `json:"remarks"`
Route string `json:"route"`
RevisionID int `json:"revision_id"`
AssignedTransponder string `json:"assigned_transponder"`
}
type DatafeedATC struct {
fsd.OnlineUserATC
Server string `json:"server"`
TextATIS []string `json:"text_atis"` // INOP placeholder
}
type DatafeedCache struct {
@@ -294,10 +353,38 @@ func (s *Server) generateDatafeed() (feed *DatafeedCache, err error) {
return
}
now := time.Now()
dataFeed := Datafeed{
Pilots: onlineUsers.Pilots,
ATC: onlineUsers.ATC,
General: DatafeedGeneral{
Version: 3,
UpdateTimestamp: now,
ConnectedClients: len(onlineUsers.Pilots) + len(onlineUsers.ATC),
UniqueUsers: len(onlineUsers.Pilots) + len(onlineUsers.ATC),
},
Pilots: []DatafeedPilot{},
ATC: []DatafeedATC{},
}
for _, pilot := range onlineUsers.Pilots {
dataFeed.Pilots = append(dataFeed.Pilots, DatafeedPilot{
OnlineUserPilot: pilot,
Server: "OPENFSD",
PilotRating: 1,
MilitaryRating: 1,
QnhIHg: 29.92,
QnhMb: 1013,
})
}
for _, atc := range onlineUsers.ATC {
dataFeed.ATC = append(dataFeed.ATC, DatafeedATC{
OnlineUserATC: atc,
Server: "OPENFSD",
TextATIS: []string{},
})
}
buf := bytes.Buffer{}
encoder := json.NewEncoder(&buf)
if err = encoder.Encode(&dataFeed); err != nil {
@@ -306,7 +393,7 @@ func (s *Server) generateDatafeed() (feed *DatafeedCache, err error) {
feed = &DatafeedCache{
jsonStr: buf.String(),
lastUpdated: time.Now(),
lastUpdated: now,
}
return
}

View File

@@ -7,6 +7,7 @@ CONNECTED CLIENTS = 1
;
;
!SERVERS:
{{ range . }}{{ .Ident }}:{{ .HostnameOrIp }}:{{ .Location }}:{{ .Ident }}:{{ .ClientsConnectionAllowed }}:{{ end }}
{{ range $index, $element := . }}{{ if $index }}
{{ end }}{{ $element.Ident }}:{{ $element.HostnameOrIp }}:{{ $element.Location }}:{{ $element.Name }}:{{ $element.ClientsConnectionAllowed }}:{{ end }}
;
; END