storage

package
v0.0.0-...-5abb9c5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 9, 2026 License: GPL-3.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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

func AuthenticateUser(ctx context.Context, u *User) context.Context

AuthenticateUser stores the user in the context for access control checks.

func ResolveSourcePath

func ResolveSourcePath(baseDir, sourcePath string) (string, error)

ResolveSourcePath resolves symlinks in a source path and returns the real path. If the path is relative, it's joined with baseDir first.

func SessionID

func SessionID(ctx context.Context) (string, bool)

SessionID retrieves the session ID from context if set.

func SetSessionID

func SetSessionID(ctx context.Context, sessionID string) context.Context

SetSessionID stores a session ID in the context for audit logging.

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) Close

func (a *AuditLogger) Close() error

Close closes the audit log file.

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

type AuditLoginFailed struct {
	User   AuditRef
	Remote string
}

AuditLoginFailed is logged on failed login attempt.

type AuditRef

type AuditRef struct {
	ID   *int64 `json:",omitempty"`
	Name string
}

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).

func Ref

func Ref(id int64, name string) AuditRef

Ref creates an AuditRef with the given ID and name.

func SystemRef

func SystemRef() AuditRef

SystemRef creates an AuditRef for "system" (no ID).

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

type AuditSessionEnd struct {
	User   AuditRef
	Remote string
}

AuditSessionEnd is logged when a session ends (disconnect/logout).

type AuditUserCreate

type AuditUserCreate struct {
	User   AuditRef
	Remote string
}

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

type AuditUserLogin struct {
	User   AuditRef
	Remote string
}

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

func NewIntervals(tree *dbm.TypeTree[structs.Interval, *structs.Interval]) *Intervals

NewIntervals creates an Intervals wrapper around an opened TypeTree.

func (*Intervals) Close

func (i *Intervals) Close() error

Close closes the underlying tree database.

func (*Intervals) CountForObject

func (i *Intervals) CountForObject(objectID string) (int, error)

CountForObject returns the number of intervals for a specific object.

func (*Intervals) Del

func (i *Intervals) Del(objectID, intervalID string) error

Del removes an interval. Returns os.ErrNotExist if not found.

func (*Intervals) DelAllForObject

func (i *Intervals) DelAllForObject(objectID string) error

DelAllForObject removes all intervals for a specific object. Returns nil if the object has no intervals.

func (*Intervals) Each

func (i *Intervals) Each() iter.Seq2[*structs.Interval, error]

Each iterates over all intervals in the database. Uses EachSet() to get all objectIDs, then SubEach() for each object's intervals.

func (*Intervals) EachForObject

func (i *Intervals) EachForObject(objectID string) iter.Seq2[*structs.Interval, error]

EachForObject iterates over all intervals for a specific object.

func (*Intervals) Get

func (i *Intervals) Get(objectID, intervalID string) (*structs.Interval, error)

Get retrieves an interval by objectID and intervalID. Returns os.ErrNotExist if not found.

func (*Intervals) Has

func (i *Intervals) Has(objectID, intervalID string) bool

Has checks if an interval exists.

func (*Intervals) Set

func (i *Intervals) Set(interval *structs.Interval) error

Set stores an interval. Updates if it already exists.

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

type MissingSource struct {
	Path      string
	ObjectIDs []string
}

MissingSource describes a source file that is missing and the objects that reference it.

type Movement

type Movement struct {
	Object      *structs.Object
	Source      string
	Destination string
}

type Refresh

type Refresh func(ctx context.Context, object *structs.Object) error

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):

  1. sourceObjectsMu (Storage-level)
  2. Object.jsMutex (JS execution serialization)
  3. Object.mutex (field access via Lock/RLock, sorted by ID via structs.WithLock)
  4. LiveTypeHash.stageMutex
  5. Tree.mutex / Hash.mutex (tkrzw internal)

func New

func New(ctx context.Context, dir string) (*Storage, error)

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

func (s *Storage) AuditLog(ctx context.Context, event string, data AuditData)

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) Close

func (s *Storage) Close() error

func (*Storage) CountSourceObjects

func (s *Storage) CountSourceObjects(ctx context.Context, path string) (int, error)

func (*Storage) CountUsers

func (s *Storage) CountUsers(ctx context.Context, filter UserFilter) (int, error)

CountUsers returns the number of users matching the filter.

func (*Storage) CreateObject

func (s *Storage) CreateObject(ctx context.Context, obj *structs.Object) error

func (*Storage) DeleteUser

func (s *Storage) DeleteUser(ctx context.Context, username string) (*User, error)

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 (s *Storage) EachObject(_ context.Context) iter.Seq2[*structs.Object, error]

func (*Storage) EachSourceObject

func (s *Storage) EachSourceObject(ctx context.Context, path string) iter.Seq2[string, error]

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

func (s *Storage) GetMostRecentLogin(ctx context.Context) (*User, error)

GetMostRecentLogin returns the user with the most recent login time. Returns nil if no users have logged in yet.

func (*Storage) ImportResolver

func (s *Storage) ImportResolver() *imports.Resolver

ImportResolver returns the import resolver for JavaScript source imports.

func (*Storage) Intervals

func (s *Storage) Intervals() *Intervals

Intervals returns the interval storage for managing recurring timers.

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

func (s *Storage) LoadResolvedSource(ctx context.Context, path string) ([]byte, int64, error)

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

func (s *Storage) LoadSource(ctx context.Context, path string) ([]byte, int64, error)

LoadSource loads a source file from the filesystem.

func (*Storage) LoadUser

func (s *Storage) LoadUser(ctx context.Context, name string) (*User, error)

func (*Storage) MoveObject

func (s *Storage) MoveObject(ctx context.Context, obj *structs.Object, destID string) error

func (*Storage) Queue

func (s *Storage) Queue() *queue.Queue

func (*Storage) RemoveObject

func (s *Storage) RemoveObject(ctx context.Context, obj *structs.Object) error

func (*Storage) RemoveSource

func (s *Storage) RemoveSource(_ context.Context, path string) error

RemoveSource removes a source file from the filesystem.

func (*Storage) ResolvedSourceModTime

func (s *Storage) ResolvedSourceModTime(ctx context.Context, path string) (int64, error)

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

func (s *Storage) SafeSourcePath(path string) (string, error)

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) SetSource

func (s *Storage) SetSource(_ context.Context, path string, content []byte) error

SetSource writes a source file to the filesystem.

func (*Storage) SetSourcesDir

func (s *Storage) SetSourcesDir(dir string)

SetSourcesDir atomically updates the sources directory. Prefer ValidateAndSwitchSources for external use to avoid TOCTOU races.

func (*Storage) SetUserWizard

func (s *Storage) SetUserWizard(ctx context.Context, username string, wizard bool) error

SetUserWizard sets the Wizard flag for a user. Only owners can modify wizard status.

func (*Storage) SourceExists

func (s *Storage) SourceExists(ctx context.Context, path string) (bool, error)

SourceExists checks if a source file exists on the filesystem.

func (*Storage) SourceModTime

func (s *Storage) SourceModTime(_ context.Context, path string) (int64, error)

SourceModTime returns the modification time of a source file.

func (*Storage) SourcesDir

func (s *Storage) SourcesDir() string

SourcesDir returns the directory where source files are stored.

func (*Storage) StoreUser

func (s *Storage) StoreUser(ctx context.Context, user *User, overwrite bool, remote string) error

func (*Storage) UNSAFEEnsureObject

func (s *Storage) UNSAFEEnsureObject(ctx context.Context, obj *structs.Object) error

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

func (s *Storage) ValidateSources(ctx context.Context, rootDir string) ([]MissingSource, error)

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

func AuthenticatedUser(ctx context.Context) (*User, bool)

AuthenticatedUser retrieves the user from context if authenticated.

func (*User) LastLogin

func (u *User) LastLogin() time.Time

LastLogin returns the last login time as a time.Time. Returns zero time if user has never logged in.

func (*User) SetLastLogin

func (u *User) SetLastLogin(t time.Time)

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)
)

Directories

Path Synopsis
Code generated by generator, DO NOT EDIT.
Code generated by generator, DO NOT EDIT.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL