Documentation
¶
Overview ¶
Package api0 implements the original master server API.
External differences:
- Proper HTTP response codes are used (this won't break anything since existing code doesn't check them).
- Caching headers are supported and used where appropriate.
- Pdiff stuff has been removed (this was never fully implemented to begin with; see docs/PDATA.md).
- Error messages have been improved. Enum values remain the same for compatibility.
- Some rate limits (no longer necessary due to increased performance and better caching) have been removed.
- More HTTP methods and features are supported (e.g., HEAD, OPTIONS, Content-Encoding).
- Website split into a separate handler (set Handler.NotFound to http.HandlerFunc(web.ServeHTTP) for identical behaviour).
- /accounts/write_persistence returns a error message for easier debugging.
- Alive/dead servers can be replaced by a new successful registration from the same ip/port. This eliminates the main cause of the duplicate server error requiring retries, and doesn't add much risk since you need to custom fuckery to start another server when you're already listening on the port.
Index ¶
- Variables
- type Account
- type AccountStorage
- type ErrorCode
- type ErrorObj
- type Handler
- func (h *Handler) CheckLauncherVersion(r *http.Request, client bool) bool
- func (h *Handler) ExtractLauncherVersion(r *http.Request) string
- func (h *Handler) Metrics() *metrics.Set
- func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (h *Handler) WritePrometheus(w io.Writer)
- func (h *Handler) WritePrometheusGeo(w io.Writer)
- type MainMenuPromos
- type MainMenuPromosButtonLarge
- type MainMenuPromosButtonSmall
- type MainMenuPromosNew
- type PdataStorage
- type Server
- type ServerList
- func (s *ServerList) DeleteServerByID(id string) bool
- func (s *ServerList) GetLiveServers(fn func(*Server) bool)
- func (s *ServerList) GetMetrics() []byte
- func (s *ServerList) GetServerByID(id string) *Server
- func (s *ServerList) ReapServers()
- func (s *ServerList) ServerHybridUpdatePut(u *ServerUpdate, c *Server, l ServerListLimit) (*Server, error)
- func (s *ServerList) VerifyServer(id string) bool
- func (s *ServerList) WritePrometheus(w io.Writer)
- func (s *ServerList) WritePrometheusGeo(w io.Writer)
- type ServerListConfig
- type ServerListLimit
- type ServerModInfo
- type ServerUpdate
Constants ¶
This section is empty.
Variables ¶
var ( ErrServerListDuplicateAuthAddr = errors.New("already have server with auth addr") ErrServerListUpdateServerDead = errors.New("no server found") ErrServerListUpdateWrongIP = errors.New("wrong server update ip") ErrServerListLimitExceeded = errors.New("would exceed server list limits") )
Functions ¶
This section is empty.
Types ¶
type Account ¶
type Account struct { // UID is the player's Origin UID. It is required and unique. UID uint64 // Username is the player's last known in-game username (their EAID). It is // optional and case insensitive. Username string // AuthIP is the IP used for the current auth session. AuthIP netip.Addr // AuthToken is the random token generated for the current auth session. AuthToken string // AuthTokenExpiry is the expiry date of the current auth token. AuthTokenExpiry time.Time // LastServerID is the ID of the last server the account connected to. LastServerID string }
Account contains information about a registered account.
func (Account) IsOnOwnServer ¶
type AccountStorage ¶
type AccountStorage interface { // GetUIDsByUsername gets all known UIDs matching username. If none match, a // nil/zero-length slice is returned. If another error occurs, err is // non-nil. GetUIDsByUsername(username string) ([]uint64, error) // GetAccount gets the player matching uid. If none exists, nil is returned. // If another error occurs, err is non-nil. GetAccount(uid uint64) (*Account, error) // SaveAccount creates or replaces an account by its uid. SaveAccount(a *Account) error }
AccountStorage stores information about registered users. It must be safe for concurrent use.
type ErrorCode ¶
type ErrorCode string
ErrorCode represents a known Northstar error code.
const ( ErrorCode_NO_GAMESERVER_RESPONSE ErrorCode = "NO_GAMESERVER_RESPONSE" // Couldn't reach game server ErrorCode_BAD_GAMESERVER_RESPONSE ErrorCode = "BAD_GAMESERVER_RESPONSE" // Game server gave an invalid response ErrorCode_UNAUTHORIZED_GAMESERVER ErrorCode = "UNAUTHORIZED_GAMESERVER" // Game server is not authorized to make that request ErrorCode_UNAUTHORIZED_GAME ErrorCode = "UNAUTHORIZED_GAME" // Stryder couldn't confirm that this account owns Titanfall 2 ErrorCode_UNAUTHORIZED_PWD ErrorCode = "UNAUTHORIZED_PWD" // Wrong password ErrorCode_STRYDER_RESPONSE ErrorCode = "STRYDER_RESPONSE" // Got bad response from stryder ErrorCode_STRYDER_PARSE ErrorCode = "STRYDER_PARSE" // Couldn't parse stryder response ErrorCode_PLAYER_NOT_FOUND ErrorCode = "PLAYER_NOT_FOUND" // Couldn't find player account ErrorCode_GAMESERVER_NOT_FOUND ErrorCode = "GAMESERVER_NOT_FOUND" // Couldn't find game server ErrorCode_INVALID_MASTERSERVER_TOKEN ErrorCode = "INVALID_MASTERSERVER_TOKEN" // Invalid or expired masterserver token ErrorCode_JSON_PARSE_ERROR ErrorCode = "JSON_PARSE_ERROR" // Error parsing json response ErrorCode_UNSUPPORTED_VERSION ErrorCode = "UNSUPPORTED_VERSION" // The version you are using is no longer supported; update Northstar to continue ErrorCode_DUPLICATE_SERVER ErrorCode = "DUPLICATE_SERVER" // A server with this port already exists for your IP address ErrorCode_CONNECTION_REJECTED ErrorCode = "CONNECTION_REJECTED" // Connection rejected )
func (ErrorCode) MessageObj ¶
MessageObj is like Message, but returns an ErrorObj.
func (ErrorCode) MessageObjf ¶
MessageObjf is like Messagef, but returns an ErrorObj.
type ErrorObj ¶
type ErrorObj struct { Code ErrorCode `json:"enum"` Message string `json:"msg"` // note: no omitempty }
ErrorObj contains an error code and a message for API responses.
type Handler ¶
type Handler struct { // ServerList stores registered servers. ServerList *ServerList // AccountStorage stores accounts. It must be non-nil. AccountStorage AccountStorage // PdataStorage stores player data. It must be non-nil. PdataStorage PdataStorage // NSPkt handles connectionless packets. It must be non-nil. NSPkt *nspkt.Listener // CleanBadWords is used to filter bad words from server names and // descriptions. If not provided, words will not be filtered. CleanBadWords func(s string) string // MainMenuPromos gets the main menu promos to return for a request. MainMenuPromos func(*http.Request) MainMenuPromos // NotFound handles requests not handled by this Handler. NotFound http.Handler // MaxServers limits the number of registered servers. If -1, no limit is // applied. If 0, a reasonable default is used. MaxServers int // MaxServersPerIP limits the number of registered servers per IP. If -1, no // limit is applied. If 0, a reasonable default is used. MaxServersPerIP int // InsecureDevNoCheckPlayerAuth is an option you shouldn't use since it // makes the server trust that clients are who they say they are. Blame // @BobTheBob9 for this option even existing in the first place. InsecureDevNoCheckPlayerAuth bool // MinimumLauncherVersion* restricts authentication and server registration // to clients with at least this version, which must be valid semver. +dev // versions are always allowed. MinimumLauncherVersionClient, MinimumLauncherVersionServer string // TokenExpiryTime controls the expiry of player masterserver auth tokens. // If zero, a reasonable a default is used. TokenExpiryTime time.Duration // AllowGameServerIPv6 controls whether to allow game servers to use IPv6. AllowGameServerIPv6 bool // LookupIP looks up an IP2Location record for an IP. If not provided, // server regions and geo metrics are disabled. If it doesn't include latlon // info, geo metrics will be disabled too. LookupIP func(netip.Addr) (ip2x.Record, error) // GetRegion gets the region name from an IP2Location record. If not // provided, server regions are disabled. // // Errors should only be returned for unexpected situations, and a // best-effort region should still be returned if applicable (it will still // be used on error if non-empty). // // Note that it is valid to return an // empty region and no error if no region is to be assigned. GetRegion func(netip.Addr, ip2x.Record) (string, error) // contains filtered or unexported fields }
Handler serves requests for the original master server API.
func (*Handler) CheckLauncherVersion ¶
CheckLauncherVersion checks if the r was made by NorthstarLauncher and if it is at least MinimumLauncherVersion.
func (*Handler) ExtractLauncherVersion ¶
ExtractLauncherVersion extracts the launcher version from r, returning an empty string if it's missing or invalid.
func (*Handler) ServeHTTP ¶
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP routes requests to Handler.
func (*Handler) WritePrometheus ¶
func (*Handler) WritePrometheusGeo ¶
type MainMenuPromos ¶
type MainMenuPromos struct { NewInfo MainMenuPromosNew `json:"newInfo"` LargeButton MainMenuPromosButtonLarge `json:"largeButton"` SmallButton1 MainMenuPromosButtonSmall `json:"smallButton1"` SmallButton2 MainMenuPromosButtonSmall `json:"smallButton2"` }
type MainMenuPromosNew ¶
type PdataStorage ¶
type PdataStorage interface { // GetPdataHash gets the current pdata hash for uid. If there is not any // pdata for uid, exists is false. If another error occurs, err is non-nil. GetPdataHash(uid uint64) (hash [sha256.Size]byte, exists bool, err error) // GetPdataCached gets the pdata for uid. If there is not any pdata for uid, // exists is false. If the provided hash is nonzero and the current pdata // matches, buf is nil. If another error occurs, err is non-nil. GetPdataCached(uid uint64, sha [sha256.Size]byte) (buf []byte, exists bool, err error) // SetPdata sets the raw pdata for uid, returning the actual size stored. SetPdata(uid uint64, buf []byte) (n int, err error) }
PdataStorage stores player data for users. It should not make any assumptions on the contents of the stored blobs (including validity). It may compress the stored data. It must be safe for concurrent use.
type Server ¶
type Server struct { Order uint64 ID string // unique, must not be modified after creation Addr netip.AddrPort // unique, must not be modified after creation AuthPort uint16 // if zero, reuse game Addr for UDP-based auth, otherwise unique with Addr.Addr(), must not be modified after creation LauncherVersion string // for metrics Name string Region string Description string Password string // blank for none Latitude float64 Longitude float64 VerificationDeadline time.Time // zero once verified LastHeartbeat time.Time PlayerCount int MaxPlayers int Map string Playlist string ServerAuthToken string // used for authenticating the masterserver to the gameserver authserver ModInfo []ServerModInfo }
type ServerList ¶
type ServerList struct {
// contains filtered or unexported fields
}
ServerList stores information about registered game servers. It does not do any validation of its own except for ensuring ID and addr/port are unique, and filtering dead/unverified/ghost servers.
func NewServerList ¶
func NewServerList(deadTime, ghostTime, verifyTime time.Duration, cfg ServerListConfig) *ServerList
NewServerList initializes a new server list.
verifyTime is the amount of time a server has to complete verification after it is created.
deadTime is the time since the last heartbeat after which a server is considered dead. A dead server will not be listed on the server list. It must be >= verifyTime if nonzero.
ghostTime is the time since the last heartbeat after which a server cannot be revived by the same ip/port combination. This allows restarted or crashed servers to recover the same server ID. Since a server must be dead to be a ghost, GhostTime must be > DeadTime for it to have any effect.
If both are nonzero, they must be positive, and deadTime must be less than ghostTime. Otherwise, NewServerList will panic.
func (*ServerList) DeleteServerByID ¶
func (s *ServerList) DeleteServerByID(id string) bool
DeleteServerByID deletes a server by its ID, returning true if a live server was deleted.
func (*ServerList) GetLiveServers ¶
func (s *ServerList) GetLiveServers(fn func(*Server) bool)
GetLiveServers loops over live (i.e., not dead/ghost) servers until fn returns false. The order of the servers is non-deterministic.
func (*ServerList) GetMetrics ¶
func (s *ServerList) GetMetrics() []byte
GetMetrics gets Prometheus text format metrics about live servers in the server list. All metrics begin with atlas_api0sl_.
Note: Playlist/map metric labels are limited to known values, or "other".
func (*ServerList) GetServerByID ¶
func (s *ServerList) GetServerByID(id string) *Server
GetServerByID returns a deep copy of the server with id, or nil if it is dead.
func (*ServerList) ReapServers ¶
func (s *ServerList) ReapServers()
ReapServers deletes dead servers from memory.
func (*ServerList) ServerHybridUpdatePut ¶
func (s *ServerList) ServerHybridUpdatePut(u *ServerUpdate, c *Server, l ServerListLimit) (*Server, error)
ServerHybridUpdatePut attempts to update a server by the server ID (if u is non-nil) (reviving it if necessary), and if that fails, then attempts to create/replace a server by the gameserver ip/port instead (if c is non-nil) while following the limits in l. It returns a copy of the resulting Server. If the resulting server's VerificationDeadline is nonzero, the server must be verified.
If the returned error is non-nil, it will either be an unavoidable internal error (e.g, failure to get random data for the server id) or one of the following (use errors.Is):
- ErrServerListDuplicateAuthAddr - if the auth ip/port of the server to create (if c) or revive (if u and server is a ghost) has already been used by a live server
- ErrServerListUpdateServerDead - if no server matching the provided id exists (if u) AND c is not provided
- ErrServerListUpdateWrongIP - if a server matching the provided id exists, but the ip doesn't match (if u and u.ExpectIP)
- ErrServerListLimitExceeded - if adding the server would exceed server limits (if c and l)
When creating a server using the values from c: c.Order, c.ID, c.ServerAuthToken, c.VerificationDeadline, and c.LastHeartbeat will be generated by this function (any existing value is ignored).
func (*ServerList) VerifyServer ¶
func (s *ServerList) VerifyServer(id string) bool
VerifyServer marks the server with the provided id as verified. If it does not exist, false is returned.
func (*ServerList) WritePrometheus ¶
func (s *ServerList) WritePrometheus(w io.Writer)
WritePrometheus writes metrics for s to w.
func (*ServerList) WritePrometheusGeo ¶
func (s *ServerList) WritePrometheusGeo(w io.Writer)
WritePrometheusGeo writes location metrics for s to w.
type ServerListConfig ¶
type ServerListConfig struct { // ExperimentalDeterministicServerIDSecret, if provided, is a secret to // combine with the server metadata upon registration to deterministically // generate a server ID. The secret is used to prevent brute-forcing server // IDs from the ID and known server info. // // This is NOT to be used for uniquely and/or persistently identifying // servers, and may change at any time. It is intended to allow servers to // have the same IDs on a best-effort basis when re-registering after // masterserver (this will reduce pdata update unauthorized errors after // restarting the masterserver) or server restart. Notable, if a server // changes their name or description, the ID will not be the same anymore. ExperimentalDeterministicServerIDSecret string AllowUwuify bool }
type ServerListLimit ¶
type ServerModInfo ¶
type ServerUpdate ¶
type ServerUpdate struct { ID string // server to update ExpectIP netip.Addr // require the server for ID to have this IP address to successfully update Heartbeat bool Name *string Region *string Description *string Latitude *float64 Longitude *float64 PlayerCount *int MaxPlayers *int Map *string Playlist *string }
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
Package api0gameserver interacts with game servers using the original master server api.
|
Package api0gameserver interacts with game servers using the original master server api. |