Skip to content
Merged
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
26 changes: 0 additions & 26 deletions .github/ISSUE_TEMPLATE/bug_report.md

This file was deleted.

39 changes: 39 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Bug report
description: Something in FivePRS isn't working
labels: [bug]
body:
- type: textarea
id: what
attributes:
label: What happened?
description: What went wrong, and what you expected to happen instead.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1. Go on duty at Mission Row
2. Open the terminal
3. ...
validations:
required: true
- type: textarea
id: diag
attributes:
label: Diagnostics
description: Run `fiveprs_diag` in the server console and paste the output.
render: text
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs
description: Any `[FivePRS]` errors or warnings from the server console, and from F8 in game if it's a client problem.
render: text
- type: textarea
id: extra
attributes:
label: Screenshots or anything else
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Documentation
url: https://fiveprs.org/docs
about: Installation, configuration and troubleshooting guides.
- name: Discord
url: https://discord.gg/zHyzHWKuM2
about: Questions and help from the community.
17 changes: 0 additions & 17 deletions .github/ISSUE_TEMPLATE/feature_request.md

This file was deleted.

20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Feature request
description: Suggest an idea for FivePRS
labels: [suggestion]
body:
- type: textarea
id: idea
attributes:
label: What would you like?
description: Describe the feature and the problem it solves.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: Anything you've tried or considered instead.
- type: textarea
id: extra
attributes:
label: Anything else
10 changes: 7 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ jobs:

- name: Build
shell: pwsh
run: ./publish.ps1 -Configuration Release
run: |
./publish.ps1 -Configuration Release
Copy-Item dist/fiveprs.zip "dist/fiveprs-$env:VERSION.zip"

- name: Test
run: dotnet test FivePRS.Tests/FivePRS.Tests.csproj -c Release --no-build
Expand All @@ -52,7 +54,9 @@ jobs:
if ($env:VERSION.Contains("-")) { $flags += "--prerelease" }
gh release view "${{ github.ref_name }}" *> $null
if ($LASTEXITCODE -eq 0) {
gh release upload "${{ github.ref_name }}" dist/fiveprs.zip --clobber
gh release upload "${{ github.ref_name }}" "dist/fiveprs-$env:VERSION.zip" --clobber
$body = gh release view "${{ github.ref_name }}" --json body --jq .body
if ([string]::IsNullOrWhiteSpace($body)) { gh release edit "${{ github.ref_name }}" --notes-file release-notes.md }
} else {
gh release create "${{ github.ref_name }}" dist/fiveprs.zip --title "FivePRS $env:VERSION" --notes-file release-notes.md @flags
gh release create "${{ github.ref_name }}" "dist/fiveprs-$env:VERSION.zip" --title "FivePRS $env:VERSION" --notes-file release-notes.md @flags
}
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [0.9.0-ptb.2] - 2026-10-10

### Added
- `fiveprs_diag` prints the version, platform, database, OneSync, addons and warnings for bug reports
- A startup summary with the version, platform and database, and warnings for problems such as OneSync being off
- The terminal's Settings app shows the FivePRS version
- Release downloads include the version in their name, like `fiveprs-0.9.0-ptb.2.zip`

### Fixed
- The server console no longer shows `Could not load assembly Windows` stack traces on start with SQLite

## [0.9.0-ptb.1] - 2026-10-10

The first public test build.
Expand Down Expand Up @@ -41,5 +52,6 @@ The first public test build.
- Only Mission Row has armory, locker room and garage points
- Back up your database before updating between test builds

[Unreleased]: https://github.com/FivePRS/core/compare/v0.9.0-ptb.1...HEAD
[Unreleased]: https://github.com/FivePRS/core/compare/v0.9.0-ptb.2...HEAD
[0.9.0-ptb.2]: https://github.com/FivePRS/core/compare/v0.9.0-ptb.1...v0.9.0-ptb.2
[0.9.0-ptb.1]: https://github.com/FivePRS/core/releases/tag/v0.9.0-ptb.1
8 changes: 8 additions & 0 deletions FivePRS.Client/Terminal/SettingsApp.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using CitizenFX.Core.Native;
using FivePRS.Client.App;
using FivePRS.Client.Dispatch;
using FivePRS.Core.Config;
Expand Down Expand Up @@ -55,6 +56,13 @@ public override AppScreen BuildScreen()
}));
}

var version = API.GetResourceMetadata(API.GetCurrentResourceName(), "version", 0);
screen.Add(AppBlock.Section("About"))
.Add(AppBlock.Paragraph($"FivePRS {version}"))
.Add(AppBlock.Paragraph(version.Contains("-")
? "Public test build. Report problems at github.com/FivePRS/core/issues"
: "fiveprs.org", true));

return screen;
}

Expand Down
22 changes: 21 additions & 1 deletion FivePRS.Core/Updates/ReleaseFeed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private sealed class GitHubRelease
PreRelease = release.PreRelease,
Version = version,
AssetUrl = release.Assets
.FirstOrDefault(asset => string.Equals(asset.Name, assetName, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(asset => IsAsset(asset.Name, assetName))
?.DownloadUrl,
}
: null)
Expand All @@ -86,6 +86,26 @@ private sealed class GitHubRelease
.FirstOrDefault();
}

public static bool IsAsset(string fileName, string assetName)
{
if (string.Equals(fileName, assetName, StringComparison.OrdinalIgnoreCase)) return true;

var dot = assetName.LastIndexOf('.');
if (dot <= 0) return false;

var prefix = assetName.Substring(0, dot) + "-";
var extension = assetName.Substring(dot);
if (fileName.Length <= prefix.Length + extension.Length ||
!fileName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) ||
!fileName.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
{
return false;
}

var version = fileName.Substring(prefix.Length, fileName.Length - prefix.Length - extension.Length);
return ReleaseVersion.TryParse(version, out _);
}

public static string Summary(string notes, int maxLines = 6)
{
var lines = notes
Expand Down
2 changes: 2 additions & 0 deletions FivePRS.Server/Dispatch/DispatchService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public DispatchService(Func<DateTime> clock, Func<ResourceSettings> settings, Fu

public int CatalogCount => _catalog.Count;

public int ActiveCallCount => _calls.Count;

public bool IsOnDuty(int unitId) => _units.ContainsKey(unitId);

public void SetOnDuty(int unitId, string name, Department department, int rank, string? agencyId = null, string? callsign = null)
Expand Down
97 changes: 97 additions & 0 deletions FivePRS.Server/ServerBrain.Diagnostics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CitizenFX.Core;
using CitizenFX.Core.Native;
using FivePRS.Core.Config;

namespace FivePRS.Server
{
public partial class ServerBrain
{
private void RegisterDiagnostics()
{
RegisterAdminCommand("fiveprs_diag", "", 0, DiagnosticsAsync);
}

private void LogStartup()
{
var version = InstalledVersion();
Debug.WriteLine($"[FivePRS] FivePRS {version} on {Platform()}, database {_databaseType} (schema {_db.SchemaVersion}).");

if (version.IsPreRelease)
Debug.WriteLine("[FivePRS] This is a public test build. Back up your database before updating, and report problems at https://github.com/FivePRS/core/issues");

foreach (var warning in StartupWarnings())
Debug.WriteLine($"[FivePRS] WARNING: {warning}");
}

private static List<string> StartupWarnings()
{
var warnings = new List<string>();

var oneSync = API.GetConvar("onesync", "off");
if (string.Equals(oneSync, "off", StringComparison.OrdinalIgnoreCase))
warnings.Add("OneSync is off. FivePRS needs OneSync for unit positions, 911 locations and vehicle registration. Set \"onesync on\" in server.cfg or txAdmin.");

var dbType = API.GetConvar("fiveprs_db_type", "sqlite");
if (!dbType.Equals("sqlite", StringComparison.OrdinalIgnoreCase) && !dbType.Equals("mysql", StringComparison.OrdinalIgnoreCase))
warnings.Add($"fiveprs_db_type \"{dbType}\" is not sqlite or mysql, so SQLite is being used.");

if (!ConfigManager.Settings.RateLimits.Enabled)
warnings.Add("Rate limits are turned off in config/settings.json, so players can flood the server with requests.");

return warnings;
}

private Task<string> DiagnosticsAsync(Player? caller, string[] args)
{
var addons = StartedAddons().ToList();
var units = _dispatch.UnitIds.Count();
var players = Players.Count();
var updates = ConfigManager.Settings.Updates;
var update = !updates.CheckForUpdates ? "checks off"
: !_updateChecked ? "not checked yet"
: _availableUpdate is null ? "up to date"
: $"{_availableUpdate.Version} available";

var lines = new List<string>
{
"FivePRS diagnostics",
$"Version: {InstalledVersion()}",
$"Server: {API.GetConvar("version", "unknown")}",
$"Platform: {Platform()}, runtime {Environment.Version}",
$"OneSync: {API.GetConvar("onesync", "off")}",
$"Database: {(_db.IsReady ? $"{_databaseType}, schema {_db.SchemaVersion}" : "not ready")}",
$"Players: {players} online, {units} on duty",
$"Dispatch: {_dispatch.CatalogCount} callouts, {_dispatch.ActiveCallCount} active calls",
$"Rate limits: {(ConfigManager.Settings.RateLimits.Enabled ? "on" : "off")}",
$"Updates: {update}",
$"Addons: {(addons.Count > 0 ? string.Join(", ", addons) : "none")}",
};

var warnings = StartupWarnings();
lines.Add(warnings.Count == 0 ? "Warnings: none" : $"Warnings: {string.Join(" | ", warnings)}");

return Task.FromResult(string.Join("\n", lines));
}

private static IEnumerable<string> StartedAddons()
{
var current = API.GetCurrentResourceName();
for (var i = 0; i < API.GetNumResources(); i++)
{
var name = API.GetResourceByFindIndex(i);
if (string.IsNullOrEmpty(name) || name == current || !name.StartsWith("fiveprs", StringComparison.OrdinalIgnoreCase)) continue;
if (API.GetResourceState(name) != "started") continue;

var version = API.GetResourceMetadata(name, "version", 0);
yield return string.IsNullOrEmpty(version) ? name : $"{name} {version}";
}
}

private static string Platform() =>
Environment.OSVersion.Platform == PlatformID.Unix ? "Linux" : "Windows";
}
}
4 changes: 4 additions & 0 deletions FivePRS.Server/ServerBrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public partial class ServerBrain : BaseScript
private readonly PermissionService _permissions;

private long _lastSnapshotAt;
private DatabaseType _databaseType;

public ServerBrain()
{
Expand Down Expand Up @@ -71,6 +72,7 @@ public ServerBrain()
RegisterTerminalEvents();
RegisterPositionEvents();
RegisterUpdates();
RegisterDiagnostics();

_ = ServerIcon.ApplyDefaultAsync(ConfigManager.Settings.Branding.ServerIcon);
_ = InitDbAsync();
Expand All @@ -84,13 +86,15 @@ private async Task InitDbAsync()
var connString = API.GetConvar("fiveprs_db_connection", "");

var dbType = dbTypeRaw == "mysql" ? DatabaseType.MySQL : DatabaseType.SQLite;
_databaseType = dbType;
await _db.InitializeAsync(dbType, string.IsNullOrEmpty(connString) ? null : connString);
_civilians = new CivilianService(_db.Civilians, () => ConfigManager.Settings, () => ConfigManager.Licenses, () => DateTime.UtcNow);
_lookup = new LookupService(_db.Civilians, () => ConfigManager.Settings, () => ConfigManager.Licenses);
_appearances = new AppearanceService(_db.Civilians, _db.Appearances, () => ConfigManager.Settings.Creator);
_roster = new RosterService(_db.Roster);
await _roster.LoadAsync();

LogStartup();
Debug.WriteLine("[FivePRS] ServerBrain online.");
}
catch (Exception ex)
Expand Down
14 changes: 14 additions & 0 deletions FivePRS.Tests/UpdateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ public void Latest_SkipsDraftsAndPreReleasesByDefault()
Assert.Equal("- One\n- Two", ReleaseFeed.Summary(release.Notes));
}

[Theory]
[InlineData("fiveprs.zip", true)]
[InlineData("FivePRS.zip", true)]
[InlineData("fiveprs-0.9.0-ptb.2.zip", true)]
[InlineData("fiveprs-1.2.3.zip", true)]
[InlineData("fiveprs-latest.zip", false)]
[InlineData("fiveprs_addons-0.9.0-ptb.2.zip", false)]
[InlineData("fiveprs-0.9.0.tar.gz", false)]
[InlineData("fiveprs-.zip", false)]
public void IsAsset_MatchesPlainAndVersionedNames(string fileName, bool expected)
{
Assert.Equal(expected, ReleaseFeed.IsAsset(fileName, "fiveprs.zip"));
}

[Fact]
public void Latest_CanIncludePreReleases()
{
Expand Down
Loading
Loading