diff --git a/build-and-push.sh b/build-and-push.sh new file mode 100644 index 0000000..ad65866 --- /dev/null +++ b/build-and-push.sh @@ -0,0 +1,2 @@ +podman manifest create $1 +podman build --platform linux/amd64,linux/arm64 --manifest $1 -f Dockerfile_fsd . diff --git a/fsd/client.go b/fsd/client.go index 2ab828e..4fd8270 100644 --- a/fsd/client.go +++ b/fsd/client.go @@ -16,7 +16,8 @@ type Client struct { cancelCtx func() sendChan chan string - lat, lon, visRange atomic.Float64 + lat, lon, visRange atomic.Float64 + closestVelocityClientDistance float64 // The closest Velocity-compatible client in meters flightPlan atomic.String assignedBeaconCode atomic.String @@ -31,7 +32,11 @@ type Client struct { facilityType int // ATC facility type. This value is only relevant for ATC loginData - authState vatsimAuthState + authState vatsimAuthState + sendFastEnabled bool +} + +type LatLon struct { } func newClient(ctx context.Context, conn net.Conn, scanner *bufio.Scanner, loginData loginData) (client *Client) { diff --git a/fsd/handler.go b/fsd/handler.go index 1f6def7..80407a8 100644 --- a/fsd/handler.go +++ b/fsd/handler.go @@ -163,6 +163,17 @@ func (s *Server) handlePilotPosition(client *Client, packet []byte) { client.heading.Store(int32(heading)) 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) + } + } } // handleFastPilotPosition handles logic for fast `^`, stopped `#ST`, and slow `#SL` pilot position updates diff --git a/fsd/postoffice.go b/fsd/postoffice.go index 3e0e77b..0438297 100644 --- a/fsd/postoffice.go +++ b/fsd/postoffice.go @@ -85,15 +85,27 @@ func (p *postOffice) updatePosition(client *Client, newCenter [2]float64, newVis return } -// search calls `callback` for every other Client within geographical range of the provided Client +// search calls `callback` for every other Client within geographical range of the provided Client. +// +// It automatically resets and populates the Client.nearbyClients and Client.closestVelocityClientDistance values func (p *postOffice) search(client *Client, callback func(recipient *Client) bool) { clientMin, clientMax := calculateBoundingBox(client.latLon(), client.visRange.Load()) + client.closestVelocityClientDistance = math.MaxFloat64 + p.treeLock.RLock() p.tree.Search(clientMin, clientMax, func(foundMin [2]float64, foundMax [2]float64, foundClient *Client) bool { if foundClient == client { return true // Ignore self } + + if foundClient.protoRevision == 101 { + dist := distance(client.lat.Load(), client.lon.Load(), foundClient.lat.Load(), foundClient.lon.Load()) + if dist < client.closestVelocityClientDistance { + client.closestVelocityClientDistance = dist + } + } + return callback(foundClient) }) p.treeLock.RUnlock() diff --git a/fsd/util.go b/fsd/util.go index 35dc8b4..8629527 100644 --- a/fsd/util.go +++ b/fsd/util.go @@ -391,3 +391,30 @@ func pitchBankHeading(packed uint32) (pitch float64, bank float64, heading float func strPtr(str string) *string { return &str } + +// sendEnableSendFastPacket sends an 'enable' $SF Send Fast packet to the client +func sendEnableSendFastPacket(client *Client) { + sendSendFastPacket(client, true) +} + +// sendDisableSendFastPacket sends a 'disable' $SF Send Fast packet to the client +func sendDisableSendFastPacket(client *Client) { + sendSendFastPacket(client, false) +} + +// sendSendFastPacket sends a $SF Send Fast packet to the client +func sendSendFastPacket(client *Client, enabled bool) { + builder := strings.Builder{} + builder.Grow(32) + builder.WriteString("$SFSERVER:") + builder.WriteString(client.callsign) + builder.WriteByte(':') + if enabled { + builder.WriteByte('1') + } else { + builder.WriteByte('0') + } + builder.WriteString("\r\n") + + client.send(builder.String()) +}