Documentation
¶
Index ¶
- Variables
- func AuthenticateUser(ctx context.Context, u *User) context.Context
- func ResolveSourcePath(baseDir, sourcePath string) (string, error)
- func SessionID(ctx context.Context) (string, bool)
- func SetSessionID(ctx context.Context, sessionID string) context.Context
- type AuditData
- type AuditEntry
- type AuditLogger
- type AuditLoginFailed
- type AuditRef
- type AuditServerConfigChange
- type AuditSessionEnd
- type AuditUserCreate
- type AuditUserDelete
- type AuditUserLogin
- type AuditWizardGrant
- type AuditWizardRevoke
- type Intervals
- func (i *Intervals) Close() error
- func (i *Intervals) CountForObject(objectID string) (int, error)
- func (i *Intervals) Del(objectID, intervalID string) error
- func (i *Intervals) DelAllForObject(objectID string) error
- func (i *Intervals) Each() iter.Seq2[*structs.Interval, error]
- func (i *Intervals) EachForObject(objectID string) iter.Seq2[*structs.Interval, error]
- func (i *Intervals) Get(objectID, intervalID string) (*structs.Interval, error)
- func (i *Intervals) Has(objectID, intervalID string) bool
- func (i *Intervals) Set(interval *structs.Interval) error
- func (i *Intervals) Update(objectID, intervalID string, ...) error
- type MissingSource
- type Movement
- type Refresh
- type Storage
- func (s *Storage) AccessObject(ctx context.Context, id string, ref Refresh) (*structs.Object, error)
- func (s *Storage) AuditLog(ctx context.Context, event string, data AuditData)
- func (s *Storage) ChangeSource(ctx context.Context, obj *structs.Object, newSourcePath string) error
- func (s *Storage) Close() error
- func (s *Storage) CountSourceObjects(ctx context.Context, path string) (int, error)
- func (s *Storage) CountUsers(ctx context.Context, filter UserFilter) (int, error)
- func (s *Storage) CreateObject(ctx context.Context, obj *structs.Object) error
- func (s *Storage) DeleteUser(ctx context.Context, username string) (*User, error)
- func (s *Storage) EachObject(_ context.Context) iter.Seq2[*structs.Object, error]
- func (s *Storage) EachSourceObject(ctx context.Context, path string) iter.Seq2[string, error]
- func (s *Storage) FlushHealth() dbm.FlushHealth
- func (s *Storage) GetMostRecentLogin(ctx context.Context) (*User, error)
- func (s *Storage) ImportResolver() *imports.Resolver
- func (s *Storage) Intervals() *Intervals
- func (s *Storage) ListUsers(ctx context.Context, filter UserFilter, sortBy UserSortField, limit int) ([]User, error)
- func (s *Storage) LoadObjects(ctx context.Context, ids map[string]bool, ref Refresh) (map[string]*structs.Object, error)
- func (s *Storage) LoadResolvedSource(ctx context.Context, path string) ([]byte, int64, error)
- func (s *Storage) LoadSource(ctx context.Context, path string) ([]byte, int64, error)
- func (s *Storage) LoadUser(ctx context.Context, name string) (*User, error)
- func (s *Storage) MoveObject(ctx context.Context, obj *structs.Object, destID string) error
- func (s *Storage) Queue() *queue.Queue
- func (s *Storage) RemoveObject(ctx context.Context, obj *structs.Object) error
- func (s *Storage) RemoveSource(_ context.Context, path string) error
- func (s *Storage) ResolvedSourceModTime(ctx context.Context, path string) (int64, error)
- func (s *Storage) SafeSourcePath(path string) (string, error)
- func (s *Storage) SetSource(_ context.Context, path string, content []byte) error
- func (s *Storage) SetSourcesDir(dir string)
- func (s *Storage) SetUserWizard(ctx context.Context, username string, wizard bool) error
- func (s *Storage) SourceExists(ctx context.Context, path string) (bool, error)
- func (s *Storage) SourceModTime(_ context.Context, path string) (int64, error)
- func (s *Storage) SourcesDir() string
- func (s *Storage) StoreUser(ctx context.Context, user *User, overwrite bool, remote string) error
- func (s *Storage) UNSAFEEnsureObject(ctx context.Context, obj *structs.Object) error
- func (s *Storage) ValidateAndSwitchSources(ctx context.Context, newDir string) ([]MissingSource, error)
- func (s *Storage) ValidateSources(ctx context.Context, rootDir string) ([]MissingSource, error)
- type User
- type UserFilter
- type UserSortField
Constants ¶
This section is empty.
Variables ¶
var ErrCircularContainer = errors.New("cannot move object into itself or its contents")
ErrCircularContainer is returned when trying to move an object into itself or into something contained within it, which would create a containment cycle.
Functions ¶
func AuthenticateUser ¶
AuthenticateUser stores the user in the context for access control checks.
func ResolveSourcePath ¶
ResolveSourcePath resolves symlinks in a source path and returns the real path. If the path is relative, it's joined with baseDir first.
Types ¶
type AuditData ¶
type AuditData interface {
// contains filtered or unexported methods
}
AuditData is the interface for typed audit event data.
type AuditEntry ¶
type AuditEntry struct {
Time string
SessionID string `json:",omitempty"`
Event string
Data AuditData
}
AuditEntry represents a single audit log entry.
type AuditLogger ¶
type AuditLogger struct {
// contains filtered or unexported fields
}
AuditLogger writes security-relevant events to a log file as JSON. Log rotation is handled automatically via lumberjack.
func NewAuditLogger ¶
func NewAuditLogger(path string) *AuditLogger
NewAuditLogger creates a new audit logger writing to the specified file with automatic log rotation.
func (*AuditLogger) Log ¶
func (a *AuditLogger) Log(ctx context.Context, event string, data AuditData)
Log writes a structured audit entry as JSON. Panics if encoding fails. This is intentional: all AuditData implementations are typed structs defined in this package with JSON-safe fields, so encoding should never fail. A failure indicates a programming error that must be fixed.
type AuditLoginFailed ¶
AuditLoginFailed is logged on failed login attempt.
type AuditRef ¶
AuditRef identifies a user by both ID and name for audit logging. ID is a pointer to distinguish between "ID is 0" and "no ID" (nil for system).
type AuditServerConfigChange ¶
type AuditServerConfigChange struct {
ChangedBy AuditRef // wizard who made the change
Path string // dot-separated path that was changed
OldValue string // JSON representation of old value
NewValue string // JSON representation of new value
}
AuditServerConfigChange is logged when the server configuration is modified.
type AuditSessionEnd ¶
AuditSessionEnd is logged when a session ends (disconnect/logout).
type AuditUserCreate ¶
AuditUserCreate is logged when a new user registers.
type AuditUserDelete ¶
type AuditUserDelete struct {
User AuditRef // user being deleted
DeletedBy AuditRef // owner who deleted the user
ObjectID string // the user's game object ID (if deleted)
}
AuditUserDelete is logged when a user is deleted.
type AuditUserLogin ¶
AuditUserLogin is logged on successful login.
type AuditWizardGrant ¶
type AuditWizardGrant struct {
Target AuditRef // user receiving wizard privileges
GrantedBy AuditRef // wizard who granted the privileges
}
AuditWizardGrant is logged when wizard privileges are granted to a user.
type AuditWizardRevoke ¶
type AuditWizardRevoke struct {
Target AuditRef // user losing wizard privileges
RevokedBy AuditRef // wizard who revoked the privileges
}
AuditWizardRevoke is logged when wizard privileges are revoked from a user.
type Intervals ¶
type Intervals struct {
// contains filtered or unexported fields
}
Intervals manages persistent storage for recurring intervals. Uses a TypeTree with objectID as the "set" and intervalID as the "key".
func NewIntervals ¶
NewIntervals creates an Intervals wrapper around an opened TypeTree.
func (*Intervals) CountForObject ¶
CountForObject returns the number of intervals for a specific object.
func (*Intervals) DelAllForObject ¶
DelAllForObject removes all intervals for a specific object. Returns nil if the object has no intervals.
func (*Intervals) Each ¶
Each iterates over all intervals in the database. Uses EachSet() to get all objectIDs, then SubEach() for each object's intervals.
func (*Intervals) EachForObject ¶
EachForObject iterates over all intervals for a specific object.
func (*Intervals) Get ¶
Get retrieves an interval by objectID and intervalID. Returns os.ErrNotExist if not found.
func (*Intervals) Update ¶
func (i *Intervals) Update(objectID, intervalID string, f func(*structs.Interval) (*structs.Interval, error)) error
Update atomically updates an interval if it exists. The function f receives the current interval and returns the updated interval (or error). If the interval doesn't exist (was cleared), f is not called and nil is returned. This avoids TOCTOU races between checking existence and updating.
type MissingSource ¶
MissingSource describes a source file that is missing and the objects that reference it.
type Storage ¶
type Storage struct {
// contains filtered or unexported fields
}
Storage manages persistent storage for the game.
Lock ordering (must always be acquired in this order to prevent deadlocks):
- sourceObjectsMu (Storage-level)
- Object.jsMutex (JS execution serialization)
- Object.mutex (field access via Lock/RLock, sorted by ID via structs.WithLock)
- LiveTypeHash.stageMutex
- Tree.mutex / Hash.mutex (tkrzw internal)
func (*Storage) AccessObject ¶
func (s *Storage) AccessObject(ctx context.Context, id string, ref Refresh) (*structs.Object, error)
AccessObject loads the object with the given ID. If a Refresh is given, it will be run if the object source is newer than the last run of the object. Refresh errors are logged but don't fail the operation - the object is still usable with old state. JS errors are also recorded in jsStats by run().
func (*Storage) AuditLog ¶
AuditLog writes a structured audit entry to the log. Note: The "remote" field in login events reflects the direct connection peer, which may be a proxy/load balancer rather than the actual client if the server is deployed behind such infrastructure.
func (*Storage) ChangeSource ¶
func (s *Storage) ChangeSource(ctx context.Context, obj *structs.Object, newSourcePath string) error
ChangeSource updates the source path for an object. PRECONDITION: Caller must have exclusive access to the object (typically via holding its lock or being the only goroutine accessing it).
func (*Storage) CountSourceObjects ¶
func (*Storage) CountUsers ¶
CountUsers returns the number of users matching the filter.
func (*Storage) CreateObject ¶
func (*Storage) DeleteUser ¶
DeleteUser removes a user from the database. Only owners can delete users. Cannot delete owners or yourself. Returns the deleted user so caller can clean up related resources (game object, connection).
func (*Storage) EachObject ¶
func (*Storage) EachSourceObject ¶
EachSourceObject iterates over all object IDs created from the given source file path. It also performs cleanup: if an object no longer exists but is still indexed under this source path, it removes the stale index entry.
Errors are yielded to callers but do not stop iteration - callers may receive errors interspersed with valid IDs and should handle both.
func (*Storage) FlushHealth ¶
func (s *Storage) FlushHealth() dbm.FlushHealth
FlushHealth returns the current health state of the object flush loop.
func (*Storage) GetMostRecentLogin ¶
GetMostRecentLogin returns the user with the most recent login time. Returns nil if no users have logged in yet.
func (*Storage) ImportResolver ¶
ImportResolver returns the import resolver for JavaScript source imports.
func (*Storage) ListUsers ¶
func (s *Storage) ListUsers(ctx context.Context, filter UserFilter, sortBy UserSortField, limit int) ([]User, error)
ListUsers returns users matching the filter, sorted by the specified field, limited to n results. If limit <= 0, returns all matching users.
func (*Storage) LoadObjects ¶
func (s *Storage) LoadObjects(ctx context.Context, ids map[string]bool, ref Refresh) (map[string]*structs.Object, error)
Loads the objects with the given IDs. If a Refresh is given, it will be run if an object source is newer than the last run of that object.
func (*Storage) LoadResolvedSource ¶
LoadResolvedSource loads a source file with all `// @import` directives resolved. Returns the concatenated source with dependencies prepended in topological order, the maximum modification time across all files in the dependency tree, and any error.
func (*Storage) LoadSource ¶
LoadSource loads a source file from the filesystem.
func (*Storage) MoveObject ¶
func (*Storage) RemoveObject ¶
func (*Storage) RemoveSource ¶
RemoveSource removes a source file from the filesystem.
func (*Storage) ResolvedSourceModTime ¶
ResolvedSourceModTime returns the maximum modification time across all files in a source's dependency tree. This is the fast path for checking if any file in the dependency chain has been modified without loading the actual source.
func (*Storage) SafeSourcePath ¶
SafeSourcePath validates a relative path and returns the full filesystem path if safe. Returns an error if the path would escape the sources directory.
func (*Storage) SetSourcesDir ¶
SetSourcesDir atomically updates the sources directory. Prefer ValidateAndSwitchSources for external use to avoid TOCTOU races.
func (*Storage) SetUserWizard ¶
SetUserWizard sets the Wizard flag for a user. Only owners can modify wizard status.
func (*Storage) SourceExists ¶
SourceExists checks if a source file exists on the filesystem.
func (*Storage) SourceModTime ¶
SourceModTime returns the modification time of a source file.
func (*Storage) SourcesDir ¶
SourcesDir returns the directory where source files are stored.
func (*Storage) UNSAFEEnsureObject ¶
UNSAFEEnsureObject creates an object if it doesn't exist, bypassing normal validation. Used only during initialization to bootstrap the world.
func (*Storage) ValidateAndSwitchSources ¶
func (s *Storage) ValidateAndSwitchSources(ctx context.Context, newDir string) ([]MissingSource, error)
ValidateAndSwitchSources validates that all source files exist in the new directory and atomically switches to it. The validation and switch are done under the same lock to prevent TOCTOU races between checking and switching. Returns missing sources if validation fails, or nil on successful switch.
Note: This function performs filesystem I/O (os.Stat) while holding locks. The sources directory should be on fast local storage to avoid contention.
func (*Storage) ValidateSources ¶
ValidateSources checks that all object source paths exist in the given root directory. Returns nil if all sources exist, or a list of missing sources with their affected objects.
type User ¶
type User struct {
Id int64 `sqly:"pkey"`
Name string `sqly:"unique"`
PasswordHash string
Owner bool
Wizard bool
Object string
LastLoginAt int64 `json:",omitempty" sqly:"index"` // Unix timestamp of last login (0 = never)
}
func AuthenticatedUser ¶
AuthenticatedUser retrieves the user from context if authenticated.
func (*User) LastLogin ¶
LastLogin returns the last login time as a time.Time. Returns zero time if user has never logged in.
func (*User) SetLastLogin ¶
SetLastLogin sets the last login time from a time.Time.
type UserFilter ¶
type UserFilter int
UserFilter specifies which users to include.
const ( UserFilterAll UserFilter = iota // All users UserFilterOwners // Only owners UserFilterWizards // Only wizards (includes owners since owners have Wizard=true) UserFilterPlayers // Only non-wizards )
type UserSortField ¶
type UserSortField int
UserSortField specifies which field to sort users by.
const ( UserSortByName UserSortField = iota // Alphabetical by username UserSortByID // By database ID (creation order) UserSortByLastLogin // Most recent login first UserSortByLastLoginAsc // Least recent login first (stale accounts) )