From 1918d8cd6dbfa88a3d899492a02d4cfb80401d2f Mon Sep 17 00:00:00 2001 From: sewn Date: Fri, 8 Mar 2024 15:58:41 +0300 Subject: [PATCH] roblox: drop for rbxbin & rbxweb --- bloxstraprpc/bloxstraprpc.go | 13 ++- bloxstraprpc/discordrpc.go | 7 +- cmd/vinegar/binary.go | 37 ++++--- cmd/vinegar/binary_setup.go | 123 +++++++++++++++-------- cmd/vinegar/main.go | 8 +- cmd/vinegar/sysinfo.go | 6 +- config/config.go | 8 +- config/config_test.go | 6 +- go.mod | 4 +- go.sum | 12 +-- internal/state/state.go | 9 -- internal/state/state_test.go | 54 ---------- roblox/api/api.go | 58 ----------- roblox/api/clientsettings.go | 27 ----- roblox/api/error.go | 36 ------- roblox/api/games.go | 62 ------------ roblox/api/thumbnails.go | 40 -------- roblox/binarytype.go | 51 ---------- roblox/bootstrapper/appsettings.go | 33 ------ roblox/bootstrapper/archive.go | 69 ------------- roblox/bootstrapper/binarydirs.go | 84 ---------------- roblox/bootstrapper/bootstrapper.go | 3 - roblox/bootstrapper/deployment.go | 45 --------- roblox/bootstrapper/mirror.go | 45 --------- roblox/bootstrapper/package.go | 74 -------------- roblox/bootstrapper/pkg_manifest.go | 108 -------------------- roblox/bootstrapper/pkg_manifest_test.go | 121 ---------------------- roblox/fflags.go | 90 ----------------- roblox/fflags_test.go | 53 ---------- roblox/roblox.go | 3 - 30 files changed, 135 insertions(+), 1154 deletions(-) delete mode 100644 internal/state/state_test.go delete mode 100644 roblox/api/api.go delete mode 100644 roblox/api/clientsettings.go delete mode 100644 roblox/api/error.go delete mode 100644 roblox/api/games.go delete mode 100644 roblox/api/thumbnails.go delete mode 100644 roblox/binarytype.go delete mode 100644 roblox/bootstrapper/appsettings.go delete mode 100644 roblox/bootstrapper/archive.go delete mode 100644 roblox/bootstrapper/binarydirs.go delete mode 100644 roblox/bootstrapper/bootstrapper.go delete mode 100644 roblox/bootstrapper/deployment.go delete mode 100644 roblox/bootstrapper/mirror.go delete mode 100644 roblox/bootstrapper/package.go delete mode 100644 roblox/bootstrapper/pkg_manifest.go delete mode 100644 roblox/bootstrapper/pkg_manifest_test.go delete mode 100644 roblox/fflags.go delete mode 100644 roblox/fflags_test.go delete mode 100644 roblox/roblox.go diff --git a/bloxstraprpc/bloxstraprpc.go b/bloxstraprpc/bloxstraprpc.go index 82e46a9b..3889a2a3 100644 --- a/bloxstraprpc/bloxstraprpc.go +++ b/bloxstraprpc/bloxstraprpc.go @@ -9,10 +9,12 @@ import ( "fmt" "log/slog" "regexp" + "strconv" "strings" "time" "github.com/altfoxie/drpc" + "github.com/apprehensions/rbxweb/games" ) const Reset = "" @@ -48,7 +50,7 @@ type Activity struct { teleporting bool server ServerType - universeID string + universeID games.UniverseID placeID string jobID string } @@ -126,8 +128,13 @@ func (a *Activity) handleGameJoinReport(line string) error { return fmt.Errorf("log game join report entry is invalid") } + uid, err := strconv.ParseInt(m[2], 10, 64) + if err != nil { + return err + } + a.placeID = m[1] - a.universeID = m[2] + a.universeID = games.UniverseID(uid) slog.Info("Handled GameJoinReport", "universeid", a.universeID, "placeid", a.placeID) @@ -163,7 +170,7 @@ func (a *Activity) handleGameLeave() error { a.gameTime = time.Time{} a.teleporting = false a.server = Public - a.universeID = "" + a.universeID = games.UniverseID(0) a.placeID = "" a.jobID = "" diff --git a/bloxstraprpc/discordrpc.go b/bloxstraprpc/discordrpc.go index c209f19b..6ec5ff8f 100644 --- a/bloxstraprpc/discordrpc.go +++ b/bloxstraprpc/discordrpc.go @@ -4,7 +4,8 @@ import ( "log/slog" "github.com/altfoxie/drpc" - "github.com/vinegarhq/vinegar/roblox/api" + "github.com/apprehensions/rbxweb/games" + "github.com/apprehensions/rbxweb/thumbnails" ) func (a *Activity) Connect() error { @@ -43,7 +44,7 @@ func (a *Activity) UpdateGamePresence(initial bool) error { if initial || (a.presence.Details == Reset || a.presence.State == Reset || a.presence.Assets.LargeText == Reset) { - gd, err := api.GetGameDetails(a.universeID) + gd, err := games.GetGameDetail(a.universeID) if err != nil { return err } @@ -69,7 +70,7 @@ func (a *Activity) UpdateGamePresence(initial bool) error { } if initial || a.presence.Assets.LargeImage == Reset { - tn, err := api.GetGameIcon(a.universeID, "PlaceHolder", "512x512", "Png", false) + tn, err := thumbnails.GetGameIcon(a.universeID, "PlaceHolder", "512x512", "Png", false) if err != nil { return err } diff --git a/cmd/vinegar/binary.go b/cmd/vinegar/binary.go index 5afc6291..f4d7f15e 100644 --- a/cmd/vinegar/binary.go +++ b/cmd/vinegar/binary.go @@ -11,6 +11,8 @@ import ( "syscall" "time" + "github.com/apprehensions/rbxbin" + "github.com/apprehensions/rbxweb/clientsettings" "github.com/fsnotify/fsnotify" "github.com/godbus/dbus/v5" "github.com/lmittmann/tint" @@ -20,8 +22,6 @@ import ( "github.com/vinegarhq/vinegar/config" "github.com/vinegarhq/vinegar/internal/dirs" "github.com/vinegarhq/vinegar/internal/state" - "github.com/vinegarhq/vinegar/roblox" - boot "github.com/vinegarhq/vinegar/roblox/bootstrapper" "github.com/vinegarhq/vinegar/splash" "github.com/vinegarhq/vinegar/sysinfo" "github.com/vinegarhq/vinegar/wine" @@ -50,23 +50,21 @@ type Binary struct { GlobalConfig *config.Config Config *config.Binary - Alias string - Name string Dir string Prefix *wine.Prefix - Type roblox.BinaryType - Deploy *boot.Deployment + Type clientsettings.BinaryType + Deploy *rbxbin.Deployment // Logging Auth bool Activity bsrpc.Activity } -func BinaryPrefixDir(bt roblox.BinaryType) string { - return filepath.Join(dirs.Prefixes, strings.ToLower(bt.String())) +func BinaryPrefixDir(bt clientsettings.BinaryType) string { + return filepath.Join(dirs.Prefixes, strings.ToLower(bt.Short())) } -func NewBinary(bt roblox.BinaryType, cfg *config.Config) (*Binary, error) { +func NewBinary(bt clientsettings.BinaryType, cfg *config.Config) (*Binary, error) { var bcfg *config.Binary var bstate *state.Binary @@ -76,10 +74,10 @@ func NewBinary(bt roblox.BinaryType, cfg *config.Config) (*Binary, error) { } switch bt { - case roblox.Player: + case clientsettings.WindowsPlayer: bcfg = &cfg.Player bstate = &s.Player - case roblox.Studio: + case clientsettings.WindowsStudio64: bcfg = &cfg.Studio bstate = &s.Studio } @@ -100,15 +98,13 @@ func NewBinary(bt roblox.BinaryType, cfg *config.Config) (*Binary, error) { GlobalConfig: cfg, Config: bcfg, - Alias: bt.String(), - Name: bt.BinaryName(), Type: bt, Prefix: pfx, }, nil } func (b *Binary) Main(args ...string) int { - logFile, err := LogFile(b.Type.String()) + logFile, err := LogFile(b.Type.Short()) if err != nil { slog.Error(fmt.Sprintf("create log file: %s", err)) return 1 @@ -199,9 +195,9 @@ func (b *Binary) Init() error { var err error switch b.Type { - case roblox.Player: + case clientsettings.WindowsPlayer: err = b.Prefix.Init() - case roblox.Studio: + case clientsettings.WindowsStudio64: // Studio accepts all DPIs except the default, which is 96. // Technically this is 'initializing wineprefix', as SetDPI calls Wine which // automatically create the Wineprefix. @@ -248,7 +244,7 @@ func (b *Binary) Execute(args ...string) error { } // Studio can run in multiple instances, not Player - if b.GlobalConfig.MultipleInstances && b.Type == roblox.Player { + if b.GlobalConfig.MultipleInstances && b.Type == clientsettings.WindowsPlayer { slog.Info("Running robloxmutexer") mutexer := b.Prefix.Wine(filepath.Join(BinPrefix, "robloxmutexer.exe")) @@ -289,8 +285,8 @@ func (b *Binary) Execute(args ...string) error { signal.Stop(c) }() - slog.Info("Running Binary", "name", b.Name, "cmd", cmd) - b.Splash.SetMessage("Launching " + b.Alias) + slog.Info("Running Binary", "name", b.Type, "cmd", cmd) + b.Splash.SetMessage("Launching " + b.Type.Short()) go func() { // Wait for process to start @@ -405,7 +401,8 @@ func (b *Binary) Command(args ...string) (*wine.Cmd, error) { args = []string{"-protocolString", args[0]} } - cmd := b.Prefix.Wine(filepath.Join(b.Dir, b.Type.Executable()), args...) + exe := "Roblox" + b.Type.Short() + "Beta.exe" + cmd := b.Prefix.Wine(filepath.Join(b.Dir, exe), args...) launcher := strings.Fields(b.Config.Launcher) if len(launcher) >= 1 { diff --git a/cmd/vinegar/binary_setup.go b/cmd/vinegar/binary_setup.go index e1526317..4c2c1700 100644 --- a/cmd/vinegar/binary_setup.go +++ b/cmd/vinegar/binary_setup.go @@ -9,16 +9,16 @@ import ( "sort" "strings" + "github.com/apprehensions/rbxbin" + "github.com/apprehensions/rbxweb/clientsettings" cp "github.com/otiai10/copy" "github.com/vinegarhq/vinegar/internal/dirs" "github.com/vinegarhq/vinegar/internal/netutil" - "github.com/vinegarhq/vinegar/roblox" - boot "github.com/vinegarhq/vinegar/roblox/bootstrapper" "github.com/vinegarhq/vinegar/wine/dxvk" "golang.org/x/sync/errgroup" ) -func (b *Binary) SetDeployment() error { +func (b *Binary) FetchDeployment() error { if b.Config.Channel != "" { slog.Warn("Channel is non-default! Only change the deployment channel if you know what you are doing!", "channel", b.Config.Channel) @@ -27,57 +27,52 @@ func (b *Binary) SetDeployment() error { if b.Config.ForcedVersion != "" { slog.Warn("Using forced deployment!", "guid", b.Config.ForcedVersion) - d := boot.NewDeployment(b.Type, b.Config.Channel, b.Config.ForcedVersion) - b.Deploy = &d + b.Deploy = &rbxbin.Deployment{ + Type: b.Type, + Channel: b.Config.Channel, + GUID: b.Config.ForcedVersion, + } return nil } - b.Splash.SetMessage("Fetching " + b.Alias) + b.Splash.SetMessage("Fetching " + b.Type.Short()) - d, err := boot.FetchDeployment(b.Type, b.Config.Channel) + d, err := rbxbin.GetDeployment(b.Type, b.Config.Channel) if err != nil { return err } - b.Deploy = &d + b.Deploy = d return nil } func (b *Binary) Setup() error { - if err := b.SetDeployment(); err != nil { - return fmt.Errorf("set %s deployment: %w", b.Config.Channel, err) + if err := b.FetchDeployment(); err != nil { + return fmt.Errorf("fetch: %w", err) } b.Dir = filepath.Join(dirs.Versions, b.Deploy.GUID) b.Splash.SetDesc(fmt.Sprintf("%s %s", b.Deploy.GUID, b.Deploy.Channel)) if b.State.Version != b.Deploy.GUID { - slog.Info("Installing Binary", "name", b.Name, + slog.Info("Installing Binary", "name", b.Type, "old_guid", b.State.Version, "new_guid", b.Deploy.GUID) if err := b.Install(); err != nil { return fmt.Errorf("install %s: %w", b.Deploy.GUID, err) } } else { - slog.Info("Binary is up to date!", "name", b.Name, "guid", b.Deploy.GUID) + slog.Info("Binary is up to date!", "name", b.Type, "guid", b.Deploy.GUID) } b.Config.Env.Setenv() - if err := b.Config.FFlags.Apply(b.Dir); err != nil { - return fmt.Errorf("apply fflags: %w", err) + if err := b.SetupOverlay(); err != nil { + return fmt.Errorf("setup overlay: %w", err) } - overlayDir := filepath.Join(dirs.Overlays, strings.ToLower(b.Type.String())) - _, err := os.Stat(overlayDir) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("stat overlay: %w", err) - } else if err == nil { - slog.Info("Copying Overlay directory's files", "src", overlayDir, "path", b.Dir) - - if err := cp.Copy(overlayDir, b.Dir); err != nil { - return fmt.Errorf("overlay dir: %w", err) - } + if err := b.Config.FFlags.Apply(b.Dir); err != nil { + return fmt.Errorf("apply fflags: %w", err) } if err := b.SetupDxvk(); err != nil { @@ -92,31 +87,56 @@ func (b *Binary) Setup() error { return nil } +func (b *Binary) SetupOverlay() error { + dir := filepath.Join(dirs.Overlays, strings.ToLower(b.Type.Short())) + + // Don't copy Overlay if it doesn't exist + _, err := os.Stat(dir) + if err != nil && errors.Is(err, os.ErrNotExist) { + return nil + } else if err != nil { + return err + } + + slog.Info("Copying Overlay directory's files", "src", dir, "path", b.Dir) + b.Splash.SetMessage("Copying Overlay") + + return cp.Copy(dir, b.Dir) +} + func (b *Binary) Install() error { - b.Splash.SetMessage("Installing " + b.Alias) + b.Splash.SetMessage("Installing " + b.Type.Short()) if err := dirs.Mkdirs(dirs.Downloads); err != nil { return err } - pm, err := boot.FetchPackageManifest(b.Deploy) + m, err := rbxbin.GetMirror() + if err != nil { + return fmt.Errorf("fetch mirror: %w", err) + } + manif, err := netutil.Body(m.PackageManifest(b.Deploy)) + if err != nil { + return fmt.Errorf("fetch packages: %w", err) + } + pkgs, err := rbxbin.ParsePackages([]byte(manif)) if err != nil { - return fmt.Errorf("fetch package manifest: %w", err) + return fmt.Errorf("parse packages: %w", err) } // Prioritize smaller files first, to have less pressure // on network and extraction // // *Theoretically*, this should be better - sort.SliceStable(pm.Packages, func(i, j int) bool { - return pm.Packages[i].ZipSize < pm.Packages[j].ZipSize + sort.SliceStable(pkgs, func(i, j int) bool { + return pkgs[i].ZipSize < pkgs[j].ZipSize }) - if err := b.SetupPackages(&pm); err != nil { + if err := b.SetupPackages(&pkgs, &m); err != nil { return fmt.Errorf("setup: %w", err) } - if b.Type == roblox.Studio { + if b.Type == clientsettings.WindowsStudio64 { brokenFont := filepath.Join(b.Dir, "StudioFonts", "SourceSansPro-Black.ttf") slog.Info("Removing broken font", "path", brokenFont) @@ -125,11 +145,14 @@ func (b *Binary) Install() error { } } - if err := boot.WriteAppSettings(b.Dir); err != nil { + if err := rbxbin.WriteAppSettings(b.Dir); err != nil { return fmt.Errorf("appsettings: %w", err) } - b.State.Add(&pm) + b.State.Version = b.Deploy.GUID + for _, pkg := range pkgs { + b.State.Packages = append(b.State.Packages, pkg.Checksum) + } if err := b.GlobalState.CleanPackages(); err != nil { return fmt.Errorf("clean packages: %w", err) @@ -142,10 +165,10 @@ func (b *Binary) Install() error { return nil } -func (b *Binary) SetupPackages(pm *boot.PackageManifest) error { - dests := boot.BinaryDirectories(b.Type) +func (b *Binary) SetupPackages(pkgs *[]rbxbin.Package, m *rbxbin.Mirror) error { + dsts := rbxbin.BinaryDirectories(b.Type) d := 0 - n := len(pm.Packages) * 2 // download & extraction + n := len(*pkgs) * 2 // download & extraction eg := new(errgroup.Group) done := func() { @@ -153,24 +176,39 @@ func (b *Binary) SetupPackages(pm *boot.PackageManifest) error { b.Splash.SetProgress(float32(d) / float32(n)) } - slog.Info("Installing Packages", "guid", pm.Deployment.GUID, "count", n) - for _, p := range pm.Packages { + slog.Info("Installing Packages", "guid", b.Deploy.GUID, "count", n) + for _, p := range *pkgs { p := p + if p.Name == "RobloxPlayerLauncher.exe" { + continue + } + eg.Go(func() error { - dest, ok := dests[p.Name] src := filepath.Join(dirs.Downloads, p.Checksum) + dst, ok := dsts[p.Name] if !ok { return fmt.Errorf("unhandled package: %s", p.Name) } - if err := p.Download(src, pm.DeployURL); err != nil { - return err + if err := p.Verify(src); err != nil { + url := m.Package(b.Deploy, p.Name) + slog.Info("Downloading package", "url", url, "path", src) + + if err := netutil.Download(url, src); err != nil { + return err + } + + if err := p.Verify(src); err != nil { + return err + } + } else { + slog.Info("Package is already downloaded", "name", p.Name, "file", src) } done() - if err := p.Extract(src, filepath.Join(b.Dir, dest)); err != nil { + if err := p.Extract(src, filepath.Join(b.Dir, dst)); err != nil { return err } done() @@ -201,6 +239,7 @@ func (b *Binary) SetupDxvk() error { dxvk.Setenv() if b.Config.DxvkVersion == b.State.DxvkVersion { + slog.Info("DXVK up to date!", "version", b.State.DxvkVersion) return nil } diff --git a/cmd/vinegar/main.go b/cmd/vinegar/main.go index 6cee823a..1a827d06 100644 --- a/cmd/vinegar/main.go +++ b/cmd/vinegar/main.go @@ -8,12 +8,12 @@ import ( "os" "path/filepath" + "github.com/apprehensions/rbxweb/clientsettings" "github.com/lmittmann/tint" "github.com/vinegarhq/vinegar/config" "github.com/vinegarhq/vinegar/config/editor" "github.com/vinegarhq/vinegar/internal/dirs" "github.com/vinegarhq/vinegar/internal/state" - "github.com/vinegarhq/vinegar/roblox" ) var ( @@ -76,12 +76,12 @@ func main() { log.Fatalf("load config %s: %s", ConfigPath, err) } - var bt roblox.BinaryType + var bt clientsettings.BinaryType switch cmd { case "player": - bt = roblox.Player + bt = clientsettings.WindowsPlayer case "studio": - bt = roblox.Studio + bt = clientsettings.WindowsStudio64 case "sysinfo": PrintSysinfo(&cfg) os.Exit(0) diff --git a/cmd/vinegar/sysinfo.go b/cmd/vinegar/sysinfo.go index 964b8a4c..6dc5e3bc 100644 --- a/cmd/vinegar/sysinfo.go +++ b/cmd/vinegar/sysinfo.go @@ -6,19 +6,19 @@ import ( "path" "runtime/debug" + "github.com/apprehensions/rbxweb/clientsettings" "github.com/vinegarhq/vinegar/config" - "github.com/vinegarhq/vinegar/roblox" "github.com/vinegarhq/vinegar/sysinfo" "github.com/vinegarhq/vinegar/wine" ) func PrintSysinfo(cfg *config.Config) { - playerPfx, err := wine.New(BinaryPrefixDir(roblox.Player), cfg.Player.WineRoot) + playerPfx, err := wine.New(BinaryPrefixDir(clientsettings.WindowsPlayer), cfg.Player.WineRoot) if err != nil { log.Fatalf("player prefix: %s", err) } - studioPfx, err := wine.New(BinaryPrefixDir(roblox.Studio), cfg.Studio.WineRoot) + studioPfx, err := wine.New(BinaryPrefixDir(clientsettings.WindowsStudio64), cfg.Studio.WineRoot) if err != nil { log.Fatalf("studio prefix: %s", err) } diff --git a/config/config.go b/config/config.go index eda81a40..5affba28 100644 --- a/config/config.go +++ b/config/config.go @@ -10,7 +10,7 @@ import ( "strings" "github.com/BurntSushi/toml" - "github.com/vinegarhq/vinegar/roblox" + "github.com/apprehensions/rbxbin" "github.com/vinegarhq/vinegar/splash" "github.com/vinegarhq/vinegar/sysinfo" "github.com/vinegarhq/vinegar/wine" @@ -30,7 +30,7 @@ type Binary struct { ForcedVersion string `toml:"forced_version"` Dxvk bool `toml:"dxvk"` DxvkVersion string `toml:"dxvk_version"` - FFlags roblox.FFlags `toml:"fflags"` + FFlags rbxbin.FFlags `toml:"fflags"` Env Environment `toml:"env"` ForcedGpu string `toml:"gpu"` GameMode bool `toml:"gamemode"` @@ -97,7 +97,7 @@ func Default() Config { Renderer: "D3D11", Channel: "", // Default upstream DiscordRPC: true, - FFlags: roblox.FFlags{ + FFlags: rbxbin.FFlags{ "DFIntTaskSchedulerTargetFps": 640, }, Env: Environment{ @@ -112,7 +112,7 @@ func Default() Config { ForcedGpu: "prime-discrete", Renderer: "D3D11", // TODO: fill with studio fflag/env goodies - FFlags: make(roblox.FFlags), + FFlags: make(rbxbin.FFlags), Env: make(Environment), }, diff --git a/config/config_test.go b/config/config_test.go index 20215b5a..0497ee78 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -6,12 +6,12 @@ import ( "os/exec" "testing" - "github.com/vinegarhq/vinegar/roblox" + "github.com/apprehensions/rbxbin" ) func TestBinarySetup(t *testing.T) { b := Binary{ - FFlags: make(roblox.FFlags), + FFlags: make(rbxbin.FFlags), Env: Environment{ "MEOW": "MEOW", }, @@ -22,7 +22,7 @@ func TestBinarySetup(t *testing.T) { } b.Renderer = "Meow" - if err := b.setup(); !errors.Is(err, roblox.ErrInvalidRenderer) { + if err := b.setup(); !errors.Is(err, rbxbin.ErrInvalidRenderer) { t.Error("expected renderer check") } diff --git a/go.mod b/go.mod index b3af6072..385127a5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/vinegarhq/vinegar -go 1.21 +go 1.22.0 require ( github.com/BurntSushi/toml v1.3.2 @@ -12,6 +12,8 @@ require ( require ( gioui.org v0.5.0 github.com/altfoxie/drpc v0.0.0-20231214171500-0a4e3a3b1c53 + github.com/apprehensions/rbxbin v0.0.0-20240308123713-798ca8013cd1 + github.com/apprehensions/rbxweb v0.0.0-20240308122818-8de3663e1d40 github.com/folbricht/pefile v0.1.0 github.com/fsnotify/fsnotify v1.7.0 github.com/godbus/dbus/v5 v5.1.0 diff --git a/go.sum b/go.sum index 1672041b..ed0b3678 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,12 @@ github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= github.com/altfoxie/drpc v0.0.0-20231214171500-0a4e3a3b1c53 h1:NcI4SrGv7yDhMRFc6SOQnmTvkOWya6WT/eXVjQoT+TA= github.com/altfoxie/drpc v0.0.0-20231214171500-0a4e3a3b1c53/go.mod h1:vV4ApNpKIGN4PT5NYmWqw1IEIsFzqj0pspTUSltS+gk= +github.com/apprehensions/rbxbin v0.0.0-20240308123713-798ca8013cd1 h1:GCQRCWzCeD7nkKSUMN9/4wizq7u7TtawCi3ntWqSiNg= +github.com/apprehensions/rbxbin v0.0.0-20240308123713-798ca8013cd1/go.mod h1:FPEDrVuql3rsAsGTXnaIvl3GXQPDxvoSeuyaQKsIFsU= +github.com/apprehensions/rbxweb v0.0.0-20240308083857-a3d7205c6de9 h1:eOEtS+YksEplRdlZSKYSzDHfpDWA7UKFVlbdTHUxzFo= +github.com/apprehensions/rbxweb v0.0.0-20240308083857-a3d7205c6de9/go.mod h1:F7WKRLrQxuRgfXxhwnlFJ059ZBMRxkXxvIhUxP4Qc5g= +github.com/apprehensions/rbxweb v0.0.0-20240308122818-8de3663e1d40 h1:YFMJMX3x/8B0VXbwA1M6JmkhtEt35VprZvj40AGRxkk= +github.com/apprehensions/rbxweb v0.0.0-20240308122818-8de3663e1d40/go.mod h1:F7WKRLrQxuRgfXxhwnlFJ059ZBMRxkXxvIhUxP4Qc5g= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/folbricht/pefile v0.1.0 h1:y9aMwgNlPO/iyp8Izll3Au4XNp7Fi7uDH8OKZ1Nl+lw= @@ -32,12 +38,8 @@ github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= -github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/copy v1.14.1-0.20240306081555-fd3129f2faa5 h1:CpkjK9QJTEEI5Z60lTCAq1KhJAX0LWnaQOI++ngsmmY= github.com/otiai10/copy v1.14.1-0.20240306081555-fd3129f2faa5/go.mod h1:DYbkL2ZWIhLKOwKhoy6SOPQPNwNwFnXFZ+W29arKx0c= -github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= -github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -59,8 +61,6 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= diff --git a/internal/state/state.go b/internal/state/state.go index 45fbe469..52fdc69c 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -7,7 +7,6 @@ import ( "path/filepath" "github.com/vinegarhq/vinegar/internal/dirs" - "github.com/vinegarhq/vinegar/roblox/bootstrapper" ) var path = filepath.Join(dirs.Data, "state.json") @@ -71,14 +70,6 @@ func (s *State) Save() error { return nil } -// Add formats the given package manifest into a Binary form. -func (bs *Binary) Add(pm *bootstrapper.PackageManifest) { - bs.Version = pm.Deployment.GUID - for _, pkg := range pm.Packages { - bs.Packages = append(bs.Packages, pkg.Checksum) - } -} - // Packages returns all the available Binary packages from the state. func (s *State) Packages() (pkgs []string) { for _, bs := range []Binary{s.Player, s.Studio} { diff --git a/internal/state/state_test.go b/internal/state/state_test.go deleted file mode 100644 index 3c92c993..00000000 --- a/internal/state/state_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package state - -import ( - "os" - "reflect" - "testing" - - "github.com/vinegarhq/vinegar/roblox" - "github.com/vinegarhq/vinegar/roblox/bootstrapper" -) - -func TestState(t *testing.T) { - f, err := os.CreateTemp(t.TempDir(), "state") - if err != nil { - t.Fatal(err) - } - - s, err := Load() - if err != nil && !reflect.DeepEqual(s, State{}) { - t.Fatal("want empty state on no file") - } - - path = f.Name() - - s, err = Load() - if err != nil { - t.Fatal(err) - } - - v := bootstrapper.NewDeployment(roblox.Player, "", "version-meowmeowmrrp") - s.Player.Add(&bootstrapper.PackageManifest{ - Deployment: &v, - Packages: bootstrapper.Packages{{ - Checksum: "meow", - }}, - }) - - if err := s.Save(); err != nil { - t.Fatal(err) - } - - sExp, err := Load() - if err != nil { - t.Fatal(err) - } - - if sExp.Player.Version != v.GUID { - t.Fatal("want version stored state") - } - - if !reflect.DeepEqual(sExp.Packages(), []string{"meow"}) { - t.Fatal("want meow packages") - } -} diff --git a/roblox/api/api.go b/roblox/api/api.go deleted file mode 100644 index 2449f13f..00000000 --- a/roblox/api/api.go +++ /dev/null @@ -1,58 +0,0 @@ -// Package api provides API routines to interact with Roblox's web API. -package api - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" -) - -const APIURL = "https://%s.roblox.com/%s" - -var httpClient = &http.Client{} - -var ( - ErrBadStatus = errors.New("bad status") - ErrNoData = errors.New("no data") -) - -// SetClient sets the http.Client used to make API requests. -func SetClient(client *http.Client) { - httpClient = client -} - -// Request makes a API request given method, service, endpoint, and data -// to send to the endpoint with the given method. -func Request(method, service, endpoint string, v interface{}) error { - url := fmt.Sprintf(APIURL, service, endpoint) - - req, err := http.NewRequest(method, url, nil) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") - - resp, err := httpClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - // Return the given API error only if the decoder succeeded - errsResp := new(errorsResponse) - if err := json.NewDecoder(resp.Body).Decode(errsResp); err == nil { - return errsResp - } - - return fmt.Errorf("%w: %s", ErrBadStatus, resp.Status) - } - - if v != nil { - return json.NewDecoder(resp.Body).Decode(v) - } - - return nil -} diff --git a/roblox/api/clientsettings.go b/roblox/api/clientsettings.go deleted file mode 100644 index 8c55cf2f..00000000 --- a/roblox/api/clientsettings.go +++ /dev/null @@ -1,27 +0,0 @@ -package api - -// ClientVersion is a representation of the Roblox ClientVersionResponse model. -type ClientVersion struct { - Version string `json:"version"` - ClientVersionUpload string `json:"clientVersionUpload"` - BootstrapperVersion string `json:"bootstrapperVersion"` - NextClientVersionUpload string `json:"nextClientVersionUpload,omitempty"` - NextClientVersion string `json:"nextClientVersion,omitempty"` -} - -// GetClientVersion gets the ClientVersion for the named binaryType and deployment channel. -func GetClientVersion(binaryType string, channel string) (ClientVersion, error) { - var cv ClientVersion - - ep := "v2/client-version/" + binaryType - if channel != "" { - ep += "/channel/" + channel - } - - err := Request("GET", "clientsettings", ep, &cv) - if err != nil { - return ClientVersion{}, err - } - - return cv, nil -} diff --git a/roblox/api/error.go b/roblox/api/error.go deleted file mode 100644 index 60058f7d..00000000 --- a/roblox/api/error.go +++ /dev/null @@ -1,36 +0,0 @@ -package api - -import ( - "fmt" - "strings" -) - -// ErrorResponse is a representation of a Roblox web API error. -type ErrorResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Field string `json:"field,omitempty"` -} - -type errorsResponse struct { - Errors []ErrorResponse `json:"errors,omitempty"` -} - -func (err ErrorResponse) Error() string { - return fmt.Sprintf("response code %d: %s", err.Code, err.Message) -} - -func (errs errorsResponse) Error() string { - s := make([]string, len(errs.Errors)) - for i, e := range errs.Errors { - s[i] = e.Error() - } - return strings.Join(s, "; ") -} - -func (errs errorsResponse) Unwrap() error { - if len(errs.Errors) == 0 { - return nil - } - return errs.Errors[0] -} diff --git a/roblox/api/games.go b/roblox/api/games.go deleted file mode 100644 index 89c1bfd2..00000000 --- a/roblox/api/games.go +++ /dev/null @@ -1,62 +0,0 @@ -package api - -import "fmt" - -// Creator is a representation of the Roblox GameCreator model. -type Creator struct { - ID int64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - IsRNVAccount bool `json:"isRNVAccount"` - HasVerifiedBadge bool `json:"hasVerifiedBadge"` -} - -// GameDetail is a representation of the Roblox GameDetailResponse model. -type GameDetail struct { - ID int64 `json:"id"` - RootPlaceID int64 `json:"rootPlaceId"` - Name string `json:"name"` - Description string `json:"description"` - SourceName string `json:"sourceName"` - SourceDescription string `json:"sourceDescription"` - Creator Creator `json:"creator"` - Price int64 `json:"price"` - AllowedGearGenres []string `json:"allowedGearGenres"` - AllowedGearCategories []string `json:"allowedGearCategories"` - IsGenreEnforced bool `json:"isGenreEnforced"` - CopyingAllowed bool `json:"copyingAllowed"` - Playing int64 `json:"playing"` - Visits int64 `json:"visits"` - MaxPlayers int32 `json:"maxPlayers"` - Created string `json:"created"` - Updated string `json:"updated"` - StudioAccessToApisAllowed bool `json:"studioAccessToApisAllowed"` - CreateVipServersAllowed bool `json:"createVipServersAllowed"` - UniverseAvatarType string `json:"universeAvatarType"` - Genre string `json:"genre"` - IsAllGenre bool `json:"isAllGenre"` - IsFavoritedByUser bool `json:"isFavoritedByUser"` - FavoritedCount int64 `json:"favoritedCount"` -} - -// GameDetail is a representation of the Roblox ApiArrayResponse GameDetailResponse model. -type GameDetailsResponse struct { - Data []GameDetail `json:"data"` -} - -// GetGameDetails gets the list of a named universeID's detail -func GetGameDetails(universeID string) (GameDetail, error) { - var gdr GameDetailsResponse - - // uids := strings.Join(universeIDs, ",") - err := Request("GET", "games", "v1/games?universeIds="+universeID, &gdr) - if err != nil { - return GameDetail{}, err - } - - if len(gdr.Data) == 0 { - return GameDetail{}, fmt.Errorf("game details: %w", ErrNoData) - } - - return gdr.Data[0], nil -} diff --git a/roblox/api/thumbnails.go b/roblox/api/thumbnails.go deleted file mode 100644 index cf22d0ef..00000000 --- a/roblox/api/thumbnails.go +++ /dev/null @@ -1,40 +0,0 @@ -package api - -import ( - "fmt" -) - -// Thumbnail is a representation of the Roblox ThumbnailResponse model. -type Thumbnail struct { - TargetID int64 `json:"targetId"` - State string `json:"state"` - ImageURL string `json:"imageUrl"` - Version string `json:"version"` -} - -// Thumbnail is a representation of the Roblox ApiArrayResponse ThumbnailResponse model. -type thumbnailResponse struct { - Data []Thumbnail `json:"data"` -} - -// GetGameIcon gets the thumbnail URL for the given universeID, refer to the -// [Thumbnails API documentation] for more information. -// -// [[Thumbnails API documentation]: https://thumbnails.roblox.com/docs/index.html -func GetGameIcon(universeID, returnPolicy, size, format string, isCircular bool) (Thumbnail, error) { - var tnr thumbnailResponse - - err := Request("GET", "thumbnails", - fmt.Sprintf("v1/games/icons?universeIds=%s&returnPolicy=%s&size=%s&format=%s&isCircular=%t", - universeID, returnPolicy, size, format, isCircular), &tnr, - ) - if err != nil { - return Thumbnail{}, err - } - - if len(tnr.Data) == 0 { - return Thumbnail{}, fmt.Errorf("thumbnails: %w", ErrNoData) - } - - return tnr.Data[0], nil -} diff --git a/roblox/binarytype.go b/roblox/binarytype.go deleted file mode 100644 index bbebb0f5..00000000 --- a/roblox/binarytype.go +++ /dev/null @@ -1,51 +0,0 @@ -package roblox - -// BinaryType is a representation of available -// Roblox applications aka. Binaries -type BinaryType int - -const ( - Player BinaryType = iota - Studio -) - -func (bt BinaryType) String() string { - switch bt { - case Player: - return "Player" - case Studio: - return "Studio" - default: - return "unknown" - } -} - -// BinaryName returns Roblox's internal API name for the -// named BinaryType -// -// Does not support platforms other than Windows. -func (bt BinaryType) BinaryName() string { - switch bt { - case Player: - return "WindowsPlayer" - case Studio: - return "WindowsStudio64" - default: - return "unknown" - } -} - -// Executable returns the executable file name for the -// named BinaryType -// -// Does not support platforms other than Windows. -func (bt BinaryType) Executable() string { - switch bt { - case Player: - return "RobloxPlayerBeta.exe" - case Studio: - return "RobloxStudioBeta.exe" - default: - return "unknown" - } -} diff --git a/roblox/bootstrapper/appsettings.go b/roblox/bootstrapper/appsettings.go deleted file mode 100644 index e6c69509..00000000 --- a/roblox/bootstrapper/appsettings.go +++ /dev/null @@ -1,33 +0,0 @@ -package bootstrapper - -import ( - "log/slog" - "os" - "path/filepath" -) - -// WriteAppSettings writes the AppSettings.xml file - required -// to run Roblox - to a binary's deployment directory. -func WriteAppSettings(dir string) error { - p := filepath.Join(dir, "AppSettings.xml") - - slog.Info("Writing AppSettings.xml", "path", p) - - f, err := os.Create(p) - if err != nil { - return err - } - defer f.Close() - - appSettings := "\r\n" + - "\r\n" + - " content\r\n" + - " http://www.roblox.com\r\n" + - "\r\n" - - if _, err := f.WriteString(appSettings); err != nil { - return err - } - - return nil -} diff --git a/roblox/bootstrapper/archive.go b/roblox/bootstrapper/archive.go deleted file mode 100644 index 391c1ba1..00000000 --- a/roblox/bootstrapper/archive.go +++ /dev/null @@ -1,69 +0,0 @@ -package bootstrapper - -import ( - "archive/zip" - "fmt" - "io" - "os" - "path/filepath" - "strings" -) - -func extract(src string, dir string) error { - r, err := zip.OpenReader(src) - if err != nil { - return err - } - defer r.Close() - - if err := os.MkdirAll(dir, 0o755); err != nil { - return err - } - - for _, f := range r.File { - dest := filepath.Join(dir, strings.ReplaceAll(f.Name, `\`, "/")) - - // ignore the destination directory, it was already created above - if dir == dest { - continue - } - - if !strings.HasPrefix(dest, filepath.Clean(dir)+string(os.PathSeparator)) { - return fmt.Errorf("illegal file path: %s", dest) - } - - if f.FileInfo().IsDir() { - if err := os.MkdirAll(dest, f.Mode()); err != nil { - return err - } - - continue - } - - if err := extractFile(f, dest); err != nil { - return err - } - } - - return nil -} - -func extractFile(src *zip.File, dest string) error { - f, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, src.Mode()) - if err != nil { - return err - } - defer f.Close() - - z, err := src.Open() - if err != nil { - return err - } - defer z.Close() - - if _, err := io.Copy(f, z); err != nil { - return err - } - - return nil -} diff --git a/roblox/bootstrapper/binarydirs.go b/roblox/bootstrapper/binarydirs.go deleted file mode 100644 index 1a1b9a40..00000000 --- a/roblox/bootstrapper/binarydirs.go +++ /dev/null @@ -1,84 +0,0 @@ -package bootstrapper - -import ( - "github.com/vinegarhq/vinegar/roblox" -) - -// PackageDirectories is a map of where Binary packages should go. -type PackageDirectories map[string]string - -// BinaryDirectories retrieves the PackageDirectories for the given [roblox.BinaryType]. -func BinaryDirectories(t roblox.BinaryType) PackageDirectories { - switch t { - case roblox.Player: - return PlayerDirectories - case roblox.Studio: - return StudioDirectories - } - - return nil -} - -// PlayerDirectories is retrieved from [Bloxstrap]. -// -// [Bloxstrap]: https://github.com/pizzaboxer/bloxstrap/blob/main/Bloxstrap/Bootstrapper.cs -var PlayerDirectories = PackageDirectories{ - "RobloxApp.zip": "", - "shaders.zip": "shaders/", - "ssl.zip": "ssl/", - "WebView2.zip": "", - "WebView2RuntimeInstaller.zip": "WebView2RuntimeInstaller", - "content-avatar.zip": "content/avatar", - "content-configs.zip": "content/configs", - "content-fonts.zip": "content/fonts", - "content-sky.zip": "content/sky", - "content-sounds.zip": "content/sounds", - "content-textures2.zip": "content/textures", - "content-models.zip": "content/models", - "content-textures3.zip": "PlatformContent/pc/textures", - "content-terrain.zip": "PlatformContent/pc/terrain", - "content-platform-fonts.zip": "PlatformContent/pc/fonts", - "extracontent-luapackages.zip": "ExtraContent/LuaPackages", - "extracontent-translations.zip": "ExtraContent/translations", - "extracontent-models.zip": "ExtraContent/models", - "extracontent-textures.zip": "ExtraContent/textures", - "extracontent-places.zip": "ExtraContent/places", -} - -// StudioDirectories is retrieved from [Roblox-Studio-Mod-Manager]. -// -// [Roblox-Studio-Mod-Manager]: https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/Config/KnownRoots.json -var StudioDirectories = PackageDirectories{ - "BuiltInPlugins.zip": "BuiltInPlugins", - "ApplicationConfig.zip": "ApplicationConfig", - "BuiltInStandalonePlugins.zip": "BuiltInStandalonePlugins", - "content-qt_translations.zip": "content/qt_translations", - "content-platform-fonts.zip": "PlatformContent/pc/fonts", - "content-terrain.zip": "PlatformContent/pc/terrain", - "content-textures3.zip": "PlatformContent/pc/textures", - "extracontent-translations.zip": "ExtraContent/translations", - "extracontent-luapackages.zip": "ExtraContent/LuaPackages", - "extracontent-textures.zip": "ExtraContent/textures", - "extracontent-scripts.zip": "ExtraContent/scripts", - "extracontent-models.zip": "ExtraContent/models", - "content-sky.zip": "content/sky", - "content-fonts.zip": "content/fonts", - "content-avatar.zip": "content/avatar", - "content-models.zip": "content/models", - "content-sounds.zip": "content/sounds", - "content-configs.zip": "content/configs", - "content-api-docs.zip": "content/api_docs", - "content-textures2.zip": "content/textures", - "content-studio_svg_textures.zip": "content/studio_svg_textures", - "Qml.zip": "Qml", - "ssl.zip": "ssl", - "Plugins.zip": "Plugins", - "shaders.zip": "shaders", - "StudioFonts.zip": "StudioFonts", - "redist.zip": "", - "WebView2.zip": "", - "Libraries.zip": "", - "LibrariesQt5.zip": "", - "RobloxStudio.zip": "", - "WebView2RuntimeInstaller.zip": "", -} diff --git a/roblox/bootstrapper/bootstrapper.go b/roblox/bootstrapper/bootstrapper.go deleted file mode 100644 index 6b28d1f8..00000000 --- a/roblox/bootstrapper/bootstrapper.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package bootstrapper implements various routines and types -// to install or bootstrap a Roblox Binary. -package bootstrapper diff --git a/roblox/bootstrapper/deployment.go b/roblox/bootstrapper/deployment.go deleted file mode 100644 index f8dbd51c..00000000 --- a/roblox/bootstrapper/deployment.go +++ /dev/null @@ -1,45 +0,0 @@ -package bootstrapper - -import ( - "log/slog" - - "github.com/vinegarhq/vinegar/roblox" - "github.com/vinegarhq/vinegar/roblox/api" -) - -// Version is a representation of a Binary's deployment or version. -// -// Channel can either be a given channel, or empty - in which Roblox -// will consider the 'default' channel. -// -// In all things related to the Roblox API, the default channel is empty, -// or 'live'/'LIVE' on clientsettings. On the Client/Studio, the default channel -// is (or can be) 'production'. This behavior is undocumented, so it's best to -// just use an empty channel i guess. -type Deployment struct { - Type roblox.BinaryType - Channel string - GUID string -} - -// NewDeployment returns a new Deployment. -func NewDeployment(bt roblox.BinaryType, channel string, GUID string) Deployment { - return Deployment{ - Type: bt, - Channel: channel, - GUID: GUID, - } -} - -// FetchDeployment returns the latest Version for the given roblox Binary type -// with the given deployment channel through [api.GetClientVersion]. -func FetchDeployment(bt roblox.BinaryType, channel string) (Deployment, error) { - slog.Info("Fetching Binary Deployment", "name", bt.BinaryName(), "channel", channel) - - cv, err := api.GetClientVersion(bt.BinaryName(), channel) - if err != nil { - return Deployment{}, err - } - - return NewDeployment(bt, channel, cv.ClientVersionUpload), nil -} diff --git a/roblox/bootstrapper/mirror.go b/roblox/bootstrapper/mirror.go deleted file mode 100644 index c06babac..00000000 --- a/roblox/bootstrapper/mirror.go +++ /dev/null @@ -1,45 +0,0 @@ -package bootstrapper - -import ( - "errors" - "log/slog" - "net/http" -) - -var ( - ErrNoMirrorFound = errors.New("no accessible deploy mirror found") - - // As of 2024-02-03: - // setup-cfly.rbxcdn.com = roblox-setup.cachefly.net - // setup.rbxcdn.com = setup-ns1.rbxcdn.com = setup-ak.rbxcdn.com - // setup-hw.rbxcdn.com = setup-ll.rbxcdn.com = does not exist - Mirrors = []string{ - // Sorted by speed - "https://setup.rbxcdn.com", - "https://setup-cfly.rbxcdn.com", - "https://s3.amazonaws.com/setup.roblox.com", - } -) - -// Mirror returns an available mirror URL from [Mirrors]. -func Mirror() (string, error) { - slog.Info("Finding an accessible deploy mirror") - - for _, m := range Mirrors { - resp, err := http.Head(m + "/" + "version") - if err != nil { - slog.Error("Bad deploy mirror", "mirror", m, "error", err) - - continue - } - resp.Body.Close() - - if resp.StatusCode == 200 { - slog.Info("Found deploy mirror", "mirror", m) - - return m, nil - } - } - - return "", ErrNoMirrorFound -} diff --git a/roblox/bootstrapper/package.go b/roblox/bootstrapper/package.go deleted file mode 100644 index 3f8422ab..00000000 --- a/roblox/bootstrapper/package.go +++ /dev/null @@ -1,74 +0,0 @@ -package bootstrapper - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "io" - "log/slog" - "os" - - "github.com/vinegarhq/vinegar/internal/netutil" -) - -// Package is a representation of a Binary package. -type Package struct { - Name string - Checksum string - Size int64 - ZipSize int64 -} - -type Packages []Package - -// Verify checks the named package source file against it's checksum -func (p *Package) Verify(src string) error { - slog.Info("Verifying Package", "name", p.Name, "path", src) - - f, err := os.Open(src) - if err != nil { - return err - } - defer f.Close() - - h := md5.New() - if _, err := io.Copy(h, f); err != nil { - return err - } - fsum := hex.EncodeToString(h.Sum(nil)) - - if p.Checksum != fsum { - return fmt.Errorf("package %s is corrupted, please re-download or delete package", p.Name) - } - - return nil -} - -// Download will download the package to the named dest destination -// directory with the given deployURL deploy mirror; if the package -// exists and has the correct checksum, it will return immediately. -func (p *Package) Download(dest, deployURL string) error { - if err := p.Verify(dest); err == nil { - slog.Info("Package is already downloaded", "name", p.Name, "file", dest) - return nil - } - - url := deployURL + "-" + p.Name - slog.Info("Downloading package", "url", url, "path", dest) - - if err := netutil.Download(url, dest); err != nil { - return fmt.Errorf("download package %s: %w", p.Name, err) - } - - return p.Verify(dest) -} - -// Extract extracts the named package source file to a given destination directory -func (p *Package) Extract(src, dest string) error { - if err := extract(src, dest); err != nil { - return fmt.Errorf("extract package %s (%s): %w", p.Name, src, err) - } - - slog.Info("Extracted package", "name", p.Name, "path", src, "dest", dest) - return nil -} diff --git a/roblox/bootstrapper/pkg_manifest.go b/roblox/bootstrapper/pkg_manifest.go deleted file mode 100644 index 59ca03a8..00000000 --- a/roblox/bootstrapper/pkg_manifest.go +++ /dev/null @@ -1,108 +0,0 @@ -package bootstrapper - -import ( - "errors" - "fmt" - "log/slog" - "strconv" - "strings" - - "github.com/vinegarhq/vinegar/internal/netutil" -) - -// PackageManifest is a representation of a Binary version's packages -// DeployURL is required, as it is where the package manifest is fetched from. -type PackageManifest struct { - *Deployment - DeployURL string - Packages -} - -var ( - ErrInvalidPkgManifest = errors.New("invalid package manifest given") - ErrUnhandledPkgManifestVer = errors.New("unhandled package manifest version") -) - -func channelPath(channel string) string { - if channel == "" { - return "/" - } - - // Ensure that the channel is lowercased, since internally in - // ClientSettings it will be lowercased, but not on the deploy mirror. - channel = strings.ToLower(channel) - - return "/channel/" + channel + "/" -} - -// FetchPackageManifest retrieves a package manifest for the given binary deployment. -func FetchPackageManifest(d *Deployment) (PackageManifest, error) { - m, err := Mirror() - if err != nil { - return PackageManifest{}, fmt.Errorf("mirror: %w", err) - } - - durl := m + channelPath(d.Channel) + d.GUID - url := durl + "-rbxPkgManifest.txt" - - slog.Info("Fetching Package Manifest", "url", url) - - smanif, err := netutil.Body(url) - if err != nil { - return PackageManifest{}, err - } - - // Because the manifest ends with also a newline, it has to be removed. - manif := strings.Split(smanif, "\r\n") - if len(manif) > 0 && manif[len(manif)-1] == "" { - manif = manif[:len(manif)-1] - } - - pkgs, err := parsePackages(manif) - if err != nil { - return PackageManifest{}, fmt.Errorf("parse: %w", err) - } - - return PackageManifest{ - Deployment: d, - DeployURL: durl, - Packages: pkgs, - }, nil -} - -func parsePackages(manifest []string) (Packages, error) { - pkgs := make(Packages, 0) - - if (len(manifest)-1)%4 != 0 { - return pkgs, ErrInvalidPkgManifest - } - - if manifest[0] != "v0" { - return pkgs, fmt.Errorf("%w: %s", ErrUnhandledPkgManifestVer, manifest[0]) - } - - for i := 1; i <= len(manifest)-4; i += 4 { - if manifest[i] == "RobloxPlayerLauncher.exe" || - manifest[i] == "WebView2RuntimeInstaller.zip" { - continue - } - - zs, err := strconv.ParseInt(manifest[i+2], 10, 64) - if err != nil { - return pkgs, err - } - s, err := strconv.ParseInt(manifest[i+3], 10, 64) - if err != nil { - return pkgs, err - } - - pkgs = append(pkgs, Package{ - Name: manifest[i], - Checksum: manifest[i+1], - Size: s, - ZipSize: zs, - }) - } - - return pkgs, nil -} diff --git a/roblox/bootstrapper/pkg_manifest_test.go b/roblox/bootstrapper/pkg_manifest_test.go deleted file mode 100644 index 0661cb41..00000000 --- a/roblox/bootstrapper/pkg_manifest_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package bootstrapper - -import ( - "errors" - "strconv" - "testing" -) - -func TestParsePackages(t *testing.T) { - manifest := []string{ - "v0", - - "foo.zip", - "026b271a21b03f2e564c036525356db5", - "71367142", - "109436874", - - "bar.zip", - "4d9ec7b52a29c80f3ce1f6a65b14b563", - "408629", - "1191394", - } - - pkgs, err := parsePackages(manifest) - if err != nil { - t.Fatal(err) - } - - pkgFooWant := Package{ - Name: "foo.zip", - Checksum: "026b271a21b03f2e564c036525356db5", - Size: 109436874, - ZipSize: 71367142, - } - - pkgBarWant := Package{ - Name: "bar.zip", - Checksum: "4d9ec7b52a29c80f3ce1f6a65b14b563", - Size: 1191394, - ZipSize: 408629, - } - - if pkgs[0] != pkgFooWant { - t.Fatalf("package %v, want package match for %v", pkgs[0], pkgFooWant) - } - - if pkgs[1] != pkgBarWant { - t.Fatalf("package %v, want package match for %v", pkgs[0], pkgBarWant) - } -} - -func TestInvalidPackagePackageManifest(t *testing.T) { - manifest := []string{ - "v0", - "foo.zip", - "026b271a21b03f2e564c036525356db5", - "71367142", - } - - _, err := parsePackages(manifest) - if !errors.Is(err, ErrInvalidPkgManifest) { - t.Fail() - } - - manifest = append(manifest, "foo") - - _, err = parsePackages(manifest) - if !errors.Is(err, strconv.ErrSyntax) { - t.Fail() - } -} - -func TestUnhandledPackagePackageManifest(t *testing.T) { - manifest := []string{ - "v1", - "foo.zip", - "026b271a21b03f2e564c036525356db5", - "71367142", - "109436874", - } - - _, err := parsePackages(manifest) - if !errors.Is(err, ErrUnhandledPkgManifestVer) { - t.Fail() - } -} - -func TestExcludedPackage(t *testing.T) { - manifest := []string{ - "v0", - - "WebView2RuntimeInstaller.zip", - "e42a6697bf05466d4dba26c8fe476d2e", - "1486447", - "1589080", - - "RobloxPlayerLauncher.exe", - "bcfb5b5e9e780e7ef4d281eb0efed185", - "4974576", - "4974576", - } - - pkgs, err := parsePackages(manifest) - if err != nil { - t.Fatal(err) - } - - if len(pkgs) != 0 { - t.Fail() - } -} - -func TestChannelPath(t *testing.T) { - if channelPath("") != "/" { - t.Fatal("expected default channel empty path") - } - - if channelPath("ZLive") != "/channel/zlive/" { - t.Fatal("expected channel path") - } -} diff --git a/roblox/fflags.go b/roblox/fflags.go deleted file mode 100644 index 0dc84373..00000000 --- a/roblox/fflags.go +++ /dev/null @@ -1,90 +0,0 @@ -package roblox - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" -) - -var ErrInvalidRenderer = errors.New("invalid renderer given") - -// defaultRenderer is used as the default renderer when -// no explicit named renderer argument has been given. -const DefaultRenderer = "D3D11" - -var renderers = []string{ - "OpenGL", - "D3D11FL10", - "D3D11", - "Vulkan", -} - -// FFlags is Roblox's Fast Flags implemented in map form. -type FFlags map[string]interface{} - -// Apply creates and compiles the FFlags file and -// directory in the named versionDir. -func (f FFlags) Apply(versionDir string) error { - dir := filepath.Join(versionDir, "ClientSettings") - path := filepath.Join(dir, "ClientAppSettings.json") - - err := os.Mkdir(dir, 0o755) - if err != nil && !errors.Is(err, os.ErrExist) { - return err - } - - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755) - if err != nil { - return err - } - defer file.Close() - - fflags, err := json.MarshalIndent(f, "", " ") - if err != nil { - return err - } - - _, err = file.Write(fflags) - if err != nil { - return err - } - - return nil -} - -// ValidRenderer determines if the named renderer is part of -// the available supported Roblox renderer backends, used in -// SetRenderer. -func ValidRenderer(renderer string) bool { - for _, r := range renderers { - if renderer == r { - return true - } - } - - return false -} - -// SetRenderer sets the named renderer to the FFlags, by disabling -// all other unused renderers. -func (f FFlags) SetRenderer(renderer string) error { - if renderer == "" { - renderer = DefaultRenderer - } - - if !ValidRenderer(renderer) { - return fmt.Errorf("fflags: %w: %s", ErrInvalidRenderer, renderer) - } - - // Disable all other renderers except the given one. - for _, r := range renderers { - isRenderer := r == renderer - - f["FFlagDebugGraphicsPrefer"+r] = isRenderer - f["FFlagDebugGraphicsDisable"+r] = !isRenderer - } - - return nil -} diff --git a/roblox/fflags_test.go b/roblox/fflags_test.go deleted file mode 100644 index 93080adb..00000000 --- a/roblox/fflags_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package roblox - -import ( - "errors" - "maps" - "testing" -) - -func TestFFlagRenderer(t *testing.T) { - f := make(FFlags) - - if err := f.SetRenderer(""); err != nil { - t.Error("expected no failure with no renderer") - } - - expectedUnset := FFlags{ - "FFlagDebugGraphicsPreferOpenGL": false, - "FFlagDebugGraphicsPreferD3D11FL10": false, - "FFlagDebugGraphicsPreferD3D11": true, - "FFlagDebugGraphicsPreferVulkan": false, - "FFlagDebugGraphicsDisableOpenGL": true, - "FFlagDebugGraphicsDisableD3D11FL10": true, - "FFlagDebugGraphicsDisableD3D11": false, - "FFlagDebugGraphicsDisableVulkan": true, - } - - if !maps.Equal(f, expectedUnset) { - t.Error("expected fflag set renderer no renderer to match expected d3d11 set") - } - - if err := f.SetRenderer("meow"); !errors.Is(err, ErrInvalidRenderer) { - t.Error("expected invalid renderer check") - } - - if err := f.SetRenderer("Vulkan"); err != nil { - t.Error("expected no failure with correct renderer") - } - - expectedSet := FFlags{ - "FFlagDebugGraphicsPreferOpenGL": false, - "FFlagDebugGraphicsPreferD3D11FL10": false, - "FFlagDebugGraphicsPreferD3D11": false, - "FFlagDebugGraphicsPreferVulkan": true, - "FFlagDebugGraphicsDisableOpenGL": true, - "FFlagDebugGraphicsDisableD3D11FL10": true, - "FFlagDebugGraphicsDisableD3D11": true, - "FFlagDebugGraphicsDisableVulkan": false, - } - - if !maps.Equal(f, expectedSet) { - t.Error("expected fflag set renderer vulkan to match expected vulkan set") - } -} diff --git a/roblox/roblox.go b/roblox/roblox.go deleted file mode 100644 index 99d33615..00000000 --- a/roblox/roblox.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package roblox provides generic types for Roblox's internal -// Binary implementations. -package roblox