Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
96a5514
add path_exists to helper lib
rawra89 Nov 23, 2025
7363052
feat: Add .adastral generation based on game version with vpk support
rawra89 Nov 23, 2025
1958369
format: Fix indentation on variables
rawra89 Nov 23, 2025
24e62c0
fix: update version generation code
rawra89 Nov 25, 2025
7f68657
chore: rename filemap variables
sour-dani Nov 28, 2025
288dc71
chore: cargo fmt
sour-dani Nov 28, 2025
9e0fd2e
chore: pretty json
sour-dani Nov 28, 2025
d5a65f1
chore: change route to `files`
sour-dani Nov 28, 2025
7ecbc68
feat: remote filemap JSON
sour-dani Nov 28, 2025
b1dd8f2
chore: include `filemap_url` key in `appvar.json`
sour-dani Nov 28, 2025
6531b0e
fix: removed duplicate API call
sour-dani Nov 28, 2025
3109735
Properly handle errors
rawra89 Dec 5, 2025
bbf0b09
fix: pass error to panic call
sour-dani Dec 19, 2025
e1b540e
fix: add function name to panic + more error handling
sour-dani Dec 19, 2025
8073668
fix: cargo fmt + more error handling
sour-dani Dec 19, 2025
635b1a5
feat: dynamic run name
sour-dani Dec 23, 2025
8a65f2d
refactor: simplified adastral file creation
sour-dani Dec 30, 2025
99702e0
fix: extended error handling
sour-dani Jan 26, 2026
e763f37
feat: new vpk related BeansErrors
sour-dani Jan 26, 2026
b7de82c
fix + docs: Error descriptions and usage notes
sour-dani Jan 26, 2026
702c92b
refactor: transition unwraps to matches with BeansError
sour-dani Jan 27, 2026
1286818
chore: removed outdated TODOs
sour-dani Jan 28, 2026
eac1ac2
fix: remote filemap search error handling
sour-dani Jan 28, 2026
d3134d0
feat: bump cargo to 1.7.4 and new credits
sour-dani Jan 28, 2026
46aee7c
docs: filemap usage
sour-dani Feb 4, 2026
eb8e516
Merge branch 'develop' into vpk
sour-dani Feb 25, 2026
8c456a5
chore: cargo fmt
sour-dani Feb 25, 2026
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
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ on:
description: 'Github Release Tag'
required: true

run-name: Release ${{ inputs.tag }}

jobs:
release:
name: release ${{ matrix.os }}
name: Release ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand Down
26 changes: 25 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,29 @@ pub async fn task_launch(&mut self, matches: &ArgMatches) {
}
}
```

### Errors
When handling errors, utilize the `BeansError` system in favor of other error instances.
Any necessary external errors should be nested inside of a new `BeansError`.
```rs
#[error("Failed to serialize provided AppVarData to JSON. ({error:})")]
AppVarDataSerializeFailure
{
error: serde_json::Error, // <-- Outside Error
data: AppVarData
},
#[error("Failed to read file attributes on {location} ({error:})")]
ReadFileAttributesError
{
error: std::io::Error,
location: String,
backtrace: Backtrace
},
#[error("Failed to open VPK file at {location} ({error:})")]
VpkOpenFailure
{
location: String,
error: anyhow::Error,
backtrace: Backtrace
}
```
**Do not make any PRs to remove the embedded executables in favor of downloading.** Some users would like to use this application offline, or they may have unreliable internet.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[package]
name = "beans-rs"
version = "1.7.3"
version = "1.7.4"
edition = "2024"
authors = [
"Kate Ward <kate@dariox.club>",
"ToastXC <contact@toastxc.xyz>"
"ToastXC <contact@toastxc.xyz>",
"Sour Dani <dani@prefortress.com>",
"dead_thing (https://github.com/rawra89)"
]
description = "Open-Source Installer for Sourcemods"
repository = "https://github.com/ktwrd/beans-rs"
Expand Down Expand Up @@ -40,6 +42,8 @@ fltk = { version = "1.5.21" }
fltk-theme = "0.7.9"
dark-light = "2.0.0"
image = { version = "0.25.8", features = ["png"] }
valve_pak = "0.1.0"
anyhow = "1.0.100"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you put a comment here saying that anyhow should never be used for returning errors, and that it should only be used as the "source" for an error
e.g:

#[error("Failed to read VPK file contents at {location}. ({error:})")]
VpkReadFailure
{
    location: String,
    error: anyhow::Error,
    backtrace: Backtrace
}

Even though it's in the CONTRIBUTING.md file, it's good to put it here anyways for future reference.


[build-dependencies]
fl2rust = "0.7.1"
Expand Down
39 changes: 38 additions & 1 deletion docs/server-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ In the `src` folder there is a file called `appvar.json` which determines the cu
},
"remote": {
"base_url": "https://of-proxy.kate.pet/",
"versions_url": "https://of-proxy.kate.pet/versions.json"
"versions_url": "https://of-proxy.kate.pet/versions.json",
"filemap_url": ""
}
}
```
Expand All @@ -25,6 +26,7 @@ In the `src` folder there is a file called `appvar.json` which determines the cu
- `name_stylized`: The full name of the mod.
- `base_url`: The file server address where the `beans-rs` related files will be stored. **Do not forget to include `/` at the end of the link.**
- `versions_url`: The file server address where the `versions.json` file will be stored.
- `filemap_url`: The file server address where the `filemap.json` file will be stored.

## beans-rs

Expand Down Expand Up @@ -96,6 +98,41 @@ The `versions` section is the data related to downloadable versions. `beans-rs`

In the `patches` section, there is only a `url`, `file` and `tempreq` entry. The `.pwr` file is a diff generated by [butler](https://itch.io/docs/butler) and is used to upgrade the user to the latest version. As of `beans-rs` version 1.7.3, the `url` component is not yet optional and if the mod is not available via torrent, the `url` component should contain the same information as `file`.

### File Mapping Variables

In `filemap.json` there are variables relating to migrating to the `.adastral` version system from a different version system.

- `version_file`: This is the location for the file that your sourcemod uses to determine its current version.
- `pack_file`: This is the optional location of the pack file (e.g. `.vpk`) if your version file is stored inside of a pack file.
- `versions`: This dictionary stores your custom versions as the `key` and the `value` as the equivalent `.adastral` version number (e.g. `"version=0.7.4": "74"`). There is no maximum or minimum amount of version entries but any entries given will be converted when `beans-rs` successfully finds a remote `filemap.json`.

### File Map Formatting

Here is an example of how to format a `filemap.json` file.

This example will be based off the [filemap.json](https://dl.prefortress.com/beans/filemap.json) file hosted by [Pre-Fortress 2](https://prefortress.com/).

```json
{
"files": {
"version_file": "version.txt",
"pack_file": "pf2_misc_dir.vpk"
},
"versions": {
"version=0.7.4": "74",
"version=0.7.3": "73",
"version=0.7.2": "72",
"version=0.7.1": "71",
"version=0.7": "70",
"version=0.6 OPEN BETA": "60"
}
}
```

The `files` dictionary must include both an entry for `version_file` and `pack_file`, but the `pack_file` entry can be left empty as it is optional.

For every entry in `versions`, the `key` must be the exact the content of your mods version file. In the example above, `version.txt` was used, but there is no set filename or content style. When `beans-rs` detects as valid version file it will convert it to a `.adastral` file with the appropriate version number. Keep in mind the Adastral version system is numeric only and cannot contain a zero at the front. For instance `0.7.4` would be converted to `74` and not `074`.

## butler

Install butler based off the instructions on the [itch.io docs](https://itch.io/docs/butler/installing.html).
Expand Down
21 changes: 11 additions & 10 deletions src/appvar.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"mod": {
"sm_name": "open_fortress",
"short_name": "of",
"name_stylized": "Open Fortress"
},
"remote": {
"base_url": "https://of-proxy.kate.pet/",
"versions_url": "https://of-proxy.kate.pet/versions.json"
}
}
"mod": {
"sm_name": "open_fortress",
"short_name": "of",
"name_stylized": "Open Fortress"
},
"remote": {
"base_url": "https://of-proxy.kate.pet/",
"versions_url": "https://of-proxy.kate.pet/versions.json",
"filemap_url": ""
}
}
6 changes: 5 additions & 1 deletion src/appvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl AppVarData
.replace("$MOD_NAME", &self.mod_info.sourcemod_name)
.replace("$URL_BASE", &self.remote_info.base_url)
.replace("$URL_VERSIONS", &self.remote_info.versions_url)
.replace("$URL_FILEMAP", &self.remote_info.filemap_url)
}

/// Try and read the data from `AVD_INSTANCE` and return when some.
Expand Down Expand Up @@ -174,5 +175,8 @@ pub struct AppVarRemote
pub base_url: String,
/// url where the version details are stored.
/// e.g; `https://beans.adastral.net/versions.json`
pub versions_url: String
pub versions_url: String,
/// optional: url where the file mapping details are stored for upgrading
/// game versions from previous systems. e.g; `https://beans.adastral.net/filemap.json`
pub filemap_url: String
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the code support this being optional? filemap.json might not be used by all mods, and it would be good if this wasn't a requirement.

Preferably it would be good for it to be Option<String> so the appvar.json doesn't need to be modified if a mod doesn't use filemap.json (like Open Fortress).

}
4 changes: 2 additions & 2 deletions src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ impl RunnerContext
Ok(Self {
sourcemod_path: parse_location(sourcemod_path.clone()),
remote_version_list: version_list,
current_version: crate::version::get_current_version(Some(sourcemod_path.clone())),
current_version: crate::version::get_current_version(Some(sourcemod_path.clone()))
.await,
appvar: AppVarData::get()
})
}
Expand Down Expand Up @@ -280,7 +281,6 @@ impl RunnerContext
}

/// Extract zstd_location to the detected sourcemods directory.
/// TODO replace unwrap/expect with match error handling
pub fn extract_package(
zstd_location: String,
out_dir: String
Expand Down
30 changes: 30 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,36 @@ pub enum BeansError
hresult_msg: String,
location: String,
backtrace: Backtrace
},

#[error("Failed to open VPK file at {location} ({error:})")]
VpkOpenFailure
{
location: String,
error: anyhow::Error,
backtrace: Backtrace
},

#[error("Failed to read VPK file contents at {location}. ({error:})")]
VpkReadFailure
{
location: String,
error: anyhow::Error,
backtrace: Backtrace
},

#[error("Failed to read packed file inside of VPK file at {location}. ({error:})")]
VpkInternalFileReadFailure
{
location: String,
error: std::io::Error,
backtrace: Backtrace
},

#[error("Failed to find local version '{expected}' in remote filemap.")]
RemoteFileMapLocalVersionNotFound
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the remote versions.json URL be included in this error?

{
expected: String
}
}
#[derive(Debug)]
Expand Down
6 changes: 6 additions & 0 deletions src/helper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ pub fn dir_exists(location: String) -> bool
file_exists(location.clone()) && is_directory(location.clone())
}

/// check if a path location exists
pub fn path_exists(path: String) -> bool
{
std::path::Path::new(&path).exists()
}

pub fn is_directory(location: String) -> bool
{
let x = PathBuf::from(&location);
Expand Down
1 change: 0 additions & 1 deletion src/helper/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use winreg::{RegKey,
use crate::{BeansError,
helper::format_directory_path};

/// TODO use windows registry to get the SourceModInstallPath
/// HKEY_CURRENT_USER\Software\Value\Steam
/// Key: SourceModInstallPath
pub fn find_sourcemod_path() -> Result<String, BeansError>
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,12 @@ pub fn get_user_agent() -> String
pub fn staging_dir() -> String
{
let av = AppVarData::get();
#[cfg(not(target_os = "windows"))] {
#[cfg(not(target_os = "windows"))]
{
format!("/butler-staging-{}", av.mod_info.short_name)
}
#[cfg(target_os = "windows")] {
#[cfg(target_os = "windows")]
{
format!("\\butler-staging-{}", av.mod_info.short_name)
}
}
Expand Down
Loading