Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions app.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"os"
"os/exec"
Expand All @@ -12,6 +13,7 @@ import (
rlbot "github.com/RLBot/go-interface"
"github.com/RLBot/go-interface/flat"
"github.com/ncruces/zenity"
"github.com/wailsapp/wails/v3/pkg/application"
)

type RawReleaseInfo struct {
Expand All @@ -21,6 +23,7 @@ type RawReleaseInfo struct {

// App struct
type App struct {
app *application.App
latestReleaseJson []RawReleaseInfo
rlbotAddress string
}
Expand Down Expand Up @@ -70,18 +73,24 @@ func (a *App) DownloadBotpack(repo string, installPath string) (string, error) {
}
}

err = DownloadExtractArchive(downloadUrl, installPath)
if downloadUrl == "" {
return "", fmt.Errorf("Failed to find %s in latest release for %s", fileName, repo)
}

err = DownloadExtractArchive(a.app.Event, downloadUrl, installPath)
if err != nil {
return "", err
}

return latestRelease.TagName, nil
}

func (a *App) RepairBotpack(repo string, installPath string) (string, error) {
err := os.RemoveAll(installPath)
if err != nil {
return "", err
func (a *App) RepairBotpack(repo string, installPath string, clearInstallPath bool) (string, error) {
if clearInstallPath {
err := os.RemoveAll(installPath)
if err != nil {
return "", err
}
}

return a.DownloadBotpack(repo, installPath)
Expand All @@ -103,11 +112,19 @@ func NewApp() *App {

var latest_release_json []RawReleaseInfo
return &App{
nil,
latest_release_json,
rlbot_address,
}
}

func (a *App) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
// You can access the application instance via ctx
a.app = application.Get()

return nil
}

func recursiveTomlSearch(root, tomlType string) ([]string, error) {
var matches []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import GuiSettings from "./components/GuiSettings.svelte";
import Home from "./pages/Home.svelte";
import RocketHost from "./pages/RocketHost.svelte";
import StoryMode from "./pages/StoryMode.svelte";
import Welcome from "./components/Welcome.svelte";
import { parseJSON } from "./index";

let activePage = $state("home");

Expand All @@ -16,6 +18,14 @@ let eventsFuture = $state(0);
let eventsVisible = $state(false);

let showGuiSettings = $state(false);

let paths: {
tagName: string | null;
repo: string | null;
installPath: string;
visible: boolean;
isDependency: boolean;
}[] = $state(parseJSON(window.localStorage.getItem("BOT_SEARCH_PATHS")) || []);
</script>

<Toaster />
Expand Down Expand Up @@ -88,7 +98,7 @@ let showGuiSettings = $state(false);
<div
class={activePage == "home" ? "pageContainer" : "pageContainer hidden"}
>
<Home />
<Home bind:paths />
</div>

<div
Expand All @@ -106,6 +116,7 @@ let showGuiSettings = $state(false);

<Events bind:visible={eventsVisible} bind:eventsNow bind:eventsFuture />
<GuiSettings bind:visible={showGuiSettings} />
<Welcome bind:paths />

<style>
main {
Expand Down
208 changes: 208 additions & 0 deletions frontend/src/components/AddBotpack.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
<script lang="ts">
import toast from "svelte-5-french-toast";
import { App } from "../../bindings/gui";
import Modal from "./Modal.svelte";
import ProgressBar from "./ProgressBar.svelte";

const OFFICIAL_BOTPACK_REPOS = [
"VirxEC/botpack-test",
"VirxEC/pytorch-archive",
];

let {
parentVisible = $bindable(false),
visible = $bindable(false),
paths = $bindable([]),
}: {
parentVisible?: boolean;
visible?: boolean;
paths?: {
tagName: string | null;
repo: string | null;
installPath: string;
visible: boolean;
isDependency: boolean;
}[];
} = $props();

let selectedBotpackType = $state("official");
let customRepo = $state("");
let installPath = $state("");

async function setDefaultPath() {
const defaultPath = await App.GetDefaultPath();
installPath = `${defaultPath}/RLBotPack`;
}

setDefaultPath();

let downloadModalTitle = $state("Downloading & extracting repo/owner");
let downloadModalVisible = $state(false);
let downloadProgress = $state(0);
let downloadCurrentStep = $state(0);
let downloadTotalSteps = $state(0);

function closeAddBotpackModal() {
downloadModalVisible = false;
visible = false;
parentVisible = true;
selectedBotpackType = "official";
customRepo = "";
setDefaultPath();
}

async function confirmAddBotpack() {
if (!installPath) {
toast.error("Install path cannot be blank");
return;
}

let repo: string, dep: string | null;

if (selectedBotpackType === "custom") {
if (!customRepo) {
toast.error("URL cannot be blank");
return;
}

if (!/^[\w-]+\/[\w-]+$/.test(customRepo)) {
toast.error("Custom repository must be in the format 'owner/repo'");
return;
}

repo = customRepo;
dep = null;
} else {
[repo, dep] = OFFICIAL_BOTPACK_REPOS;
}

if (paths.some((x) => x.installPath === installPath)) {
toast.error(`Install path "${installPath}" already in use for ${repo}`);
return;
}

if (paths.some((x) => x.repo === repo)) {
toast.error(`Botpack ${repo} already added`);
return;
}

downloadModalTitle = `Downloading & extracting ${repo}`;
downloadProgress = 0;
visible = false;
downloadModalVisible = true;
downloadCurrentStep = 0;
downloadTotalSteps = dep ? 4 : 2;

const tagName = await App.DownloadBotpack(repo, installPath).catch((err) => {
toast.error(`Failed to download botpack: ${err}`, {
duration: 10000,
});

return null;
});
if (!tagName) {
downloadModalVisible = false;
visible = true;
return;
}

if (dep) {
downloadModalTitle = `Downloading & extracting ${dep}`;
downloadProgress = 0;
downloadCurrentStep += 1;

// Download possible dependency of the given botpack
const tagName = await App.DownloadBotpack(dep, installPath).catch((err) => {
toast.error(`Failed to download botpack dependency: ${err}`, {
duration: 10000,
});

return null;
});
if (!tagName) {
downloadModalVisible = false;
visible = true;
return;
}
}

paths.push({
installPath,
repo,
tagName,
visible: true,
isDependency: false,
});
if (dep) {
paths.push({
installPath,
repo: dep,
tagName,
visible: false,
isDependency: true,
});
}

toast.success("Botpack downloaded successfully!");
closeAddBotpackModal();
}
</script>

<Modal title="Add Botpack" bind:visible={visible}>
<div class="add-botpack">
<label for="path">Botpack install path</label>
<input type="text" id="path" placeholder="Enter install path" bind:value={installPath} />
<div class="radio-group">
<label>
<input type="radio" name="botpackType" value="official" bind:group={selectedBotpackType} />
Official RLBotPack
</label>
<label>
<input type="radio" name="botpackType" value="custom" bind:group={selectedBotpackType} />
Custom
</label>
</div>
{#if selectedBotpackType === "custom"}
<input type="text" placeholder="owner/repo" bind:value={customRepo} />
{/if}
<div class="button-row">
<button onclick={confirmAddBotpack}>Confirm</button>
<button onclick={closeAddBotpackModal}>Cancel</button>
</div>
</div>
</Modal>

<Modal title={downloadModalTitle} bind:visible={downloadModalVisible} closeable={false}>
<ProgressBar bind:percentComplete={downloadProgress} bind:currentStep={downloadCurrentStep} totalSteps={downloadTotalSteps} />
</Modal>

<style>
.button-row {
display: flex;
gap: 1rem;
}
.button-row {
display: flex;
gap: 1rem;
justify-content: flex-end;
}
.button-row button {
flex: 1;
}
.add-botpack {
display: flex;
flex-direction: column;
gap: 1rem;
min-width: 50vw;
}
.radio-group {
flex-direction: column;
display: flex;
gap: 1rem;
}
.radio-group label {
display: flex;
align-items: center;
gap: 0.5rem;
}
</style>
3 changes: 2 additions & 1 deletion frontend/src/components/Events.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ async function fetchEvents() {
.event {
display: flex;
width: 100%;
max-width: 1280px;
align-items: center;
justify-content: space-between;
gap: 1rem;
Expand All @@ -249,7 +250,7 @@ async function fetchEvents() {
display: flex;
justify-content: center;
align-items: center;
width: 18%;
width: 20%;
height: 100%;
}
.event-logo {
Expand Down
23 changes: 8 additions & 15 deletions frontend/src/components/LauncherSelector.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<script lang="ts">
import Modal from "./Modal.svelte";
import NiceSelect from "./NiceSelect.svelte";

let { visible = $bindable() } = $props();

let localLauncher = $state(localStorage.getItem("MS_LAUNCHER") || "");
let localLauncherArg = $state(localStorage.getItem("MS_LAUNCHER_ARG") || "");

Expand Down Expand Up @@ -51,19 +48,15 @@ $effect(() => {
});
</script>

<button onclick={() => { visible = true }}>Launcher Options</button>

<Modal title="Select a launcher" bind:visible>
<div class="container">
<NiceSelect bind:value={launcher} options={launcherOptions} placeholder="Select a launcher" />
{#if launcher === "custom"}
<div class="launcherArg">
<label for="launcherArg">Additional argument:</label>
<input type="text" id="launcherArg" bind:value={localLauncherArg} placeholder="(Leave blank for default)">
</div>
{/if}
<div class="container">
<NiceSelect bind:value={launcher} options={launcherOptions} placeholder="Select a launcher" />
{#if launcher === "custom"}
<div class="launcherArg">
<label for="launcherArg">Additional argument:</label>
<input type="text" id="launcherArg" bind:value={localLauncherArg} placeholder="(Leave blank for default)">
</div>
</Modal>
{/if}
</div>

<style>
.container {
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/MatchSettings/Main.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const ALL_MAPS = getMaps();
/>
</div>
<div class="right-controls">
<LauncherSelector bind:visible={launcherOptionsVisible} />
<button onclick={() => { launcherOptionsVisible = true }}>Launcher Options</button>
</div>
</div>
<div class="controls">
Expand Down Expand Up @@ -147,6 +147,10 @@ const ALL_MAPS = getMaps();
</div>
</div>

<Modal title="Select a launcher" bind:visible={launcherOptionsVisible}>
<LauncherSelector />
</Modal>

<Modal title="Rocket League Mutators" bind:visible={showMutators}>
<div class="mutators">
{#each filteredMutatorOptions as mutatorKey}
Expand Down
Loading