feat(all): add resilient server recovery and talent upgrades

This commit is contained in:
2026-10-03 15:09:31 +08:00
parent db1eab764e
commit ce03fe7289
70 changed files with 3664 additions and 419 deletions
+19 -5
View File
@@ -14,15 +14,18 @@ import (
const ReleaseFileName = "versions.json"
var (
clientVersionPattern = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+$`)
gameVersionPattern = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+$`)
releaseVersionPattern = regexp.MustCompile(`^([0-9]+\.[0-9]+\.[0-9]+)\+(client|server)\.([0-9]+\.[0-9]+\.[0-9]+)$`)
resourceVersionPattern = regexp.MustCompile(`^[0-9]{14}$`)
)
// ReleaseVersions is the exact client/resource tuple supported by one
// bd2client distribution. The release package carries the authoritative
// versions.json next to bd2client.exe.
// ReleaseVersions separates the official game compatibility version from the
// independently evolving client and server software release versions. The
// release package carries the authoritative versions.json beside bd2client.
type ReleaseVersions struct {
GameVersion string `json:"game_version"`
ClientVersion string `json:"client_version"`
ServerVersion string `json:"server_version"`
GameDataVersion string `json:"game_data_version"`
BundleVersion string `json:"bundle_version"`
SeedDirectory string `json:"seed_directory"`
@@ -48,9 +51,15 @@ func LoadReleaseVersions(path string) (ReleaseVersions, error) {
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
return ReleaseVersions{}, errors.New("client release versions must contain exactly one JSON object")
}
if !clientVersionPattern.MatchString(versions.ClientVersion) {
if !gameVersionPattern.MatchString(versions.GameVersion) {
return ReleaseVersions{}, fmt.Errorf("invalid game_version %q", versions.GameVersion)
}
if !validReleaseVersion(versions.ClientVersion, versions.GameVersion, "client") {
return ReleaseVersions{}, fmt.Errorf("invalid client_version %q", versions.ClientVersion)
}
if !validReleaseVersion(versions.ServerVersion, versions.GameVersion, "server") {
return ReleaseVersions{}, fmt.Errorf("invalid server_version %q", versions.ServerVersion)
}
if !resourceVersionPattern.MatchString(versions.BundleVersion) || !resourceVersionPattern.MatchString(versions.GameDataVersion) {
return ReleaseVersions{}, errors.New("bundle_version and game_data_version must be 14-digit timestamps")
}
@@ -61,6 +70,11 @@ func LoadReleaseVersions(path string) (ReleaseVersions, error) {
return versions, nil
}
func validReleaseVersion(value, game, component string) bool {
match := releaseVersionPattern.FindStringSubmatch(value)
return len(match) == 4 && match[1] == game && match[2] == component
}
func ReleaseVersionsBesideExecutable() (ReleaseVersions, error) {
executable, err := os.Executable()
if err != nil {