feat(all): add hunting and events, commerce and rewards, binary calendars, GameData caching and login diagnostics
Implement hunting AP and progress, event tasks, exchanges, games, stories and actions; connect battle and reward handling. Add CashShop plugin and installation, server purchases and entitlements, recharge rules and plugin version metadata. Distribute binary calendars independently; cache GameData in memory and fix single-connection query deadlocks. Improve automatic login and recovery diagnostics; document configuration and release flows.
This commit is contained in:
@@ -30,6 +30,7 @@ type Options struct {
|
||||
Versions clientconfig.ReleaseVersions
|
||||
LocalIdentityPlugin string
|
||||
LoginUIPlugin string
|
||||
CashShopPlugin string
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
@@ -266,7 +267,7 @@ func (s *Studio) Patch(input Request) (Response, error) {
|
||||
|
||||
func (s *Studio) Install(input Request) (Response, error) {
|
||||
return s.perform("install plugins", func() (Response, error) {
|
||||
result, err := clientsetup.InstallPlugins(input.GameDirectory, input.settings(), s.options.Versions, s.options.LocalIdentityPlugin, s.options.LoginUIPlugin)
|
||||
result, err := clientsetup.InstallPlugins(input.GameDirectory, input.settings(), s.options.Versions, s.options.LocalIdentityPlugin, s.options.LoginUIPlugin, s.options.CashShopPlugin)
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
@@ -274,7 +275,7 @@ func (s *Studio) Install(input Request) (Response, error) {
|
||||
return Response{}, err
|
||||
}
|
||||
message := "BD2 client plugins installed or updated"
|
||||
if !result.LocalIdentity.Changed && !result.LoginUI.Changed {
|
||||
if !result.LocalIdentity.Changed && !result.LoginUI.Changed && !result.CashShop.Changed {
|
||||
message = "BD2 client plugins are already up to date; no DLL was rewritten"
|
||||
}
|
||||
return success(message, result), nil
|
||||
@@ -312,7 +313,7 @@ func (s *Studio) Launch(input Request) (Response, error) {
|
||||
if !installation.SupportedOnHost() {
|
||||
return Response{}, fmt.Errorf("cannot launch a %s game client from this operating system", installation.Kind)
|
||||
}
|
||||
for _, name := range []string{"BD2LocalIdentity.dll", "BD2LoginUI.dll"} {
|
||||
for _, name := range []string{"BD2LocalIdentity.dll", "BD2LoginUI.dll", "BD2CashShop.dll"} {
|
||||
info, err := os.Stat(filepath.Join(installation.Plugins, name))
|
||||
if err != nil || !info.Mode().IsRegular() {
|
||||
return Response{}, fmt.Errorf("install or update the client plugins before launching; %s is missing", name)
|
||||
|
||||
@@ -33,6 +33,7 @@ type ReleaseVersions struct {
|
||||
LocalIdentity string `json:"local_identity"`
|
||||
CaptureEnvironment string `json:"capture_environment"`
|
||||
LoginUI string `json:"login_ui"`
|
||||
CashShop string `json:"cash_shop"`
|
||||
} `json:"plugins"`
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ type Spec struct {
|
||||
var (
|
||||
LocalIdentity = Spec{fileName: "BD2LocalIdentity.dll"}
|
||||
LoginUI = Spec{fileName: "BD2LoginUI.dll"}
|
||||
CashShop = Spec{fileName: "BD2CashShop.dll"}
|
||||
GameNames = Spec{fileName: "BD2.GameNames.dll"}
|
||||
)
|
||||
|
||||
@@ -84,7 +85,7 @@ func Install(spec Spec, gameDir, source string) (Result, error) {
|
||||
// Read both artifacts before touching the installation. The runtime table
|
||||
// library is shipped beside every plugin, including explicit development paths.
|
||||
var runtimeData []byte
|
||||
if spec == LocalIdentity || spec == LoginUI {
|
||||
if spec == LocalIdentity || spec == LoginUI || spec == CashShop {
|
||||
runtimeSource := filepath.Join(filepath.Dir(source), GameNames.fileName)
|
||||
runtimeData, err = os.ReadFile(runtimeSource)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func TestInstallRequiresBepInExWithoutCopyingPlugin(t *testing.T) {
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI} {
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI, CashShop} {
|
||||
t.Run(spec.FileName(), func(t *testing.T) {
|
||||
gameDir := t.TempDir()
|
||||
for path, data := range map[string][]byte{
|
||||
@@ -41,7 +41,7 @@ func TestInstallRequiresBepInExWithoutCopyingPlugin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstallCopiesUpdatesAndSkipsIdenticalPlugin(t *testing.T) {
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI} {
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI, CashShop} {
|
||||
t.Run(spec.FileName(), func(t *testing.T) {
|
||||
gameDir := t.TempDir()
|
||||
for path, data := range map[string][]byte{
|
||||
@@ -140,7 +140,7 @@ func TestInstallKeepsPluginsSeparate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI} {
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI, CashShop} {
|
||||
source := filepath.Join(t.TempDir(), spec.FileName())
|
||||
if err := os.WriteFile(filepath.Join(filepath.Dir(source), GameNames.FileName()), []byte("names-v1"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -152,7 +152,7 @@ func TestInstallKeepsPluginsSeparate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI} {
|
||||
for _, spec := range []Spec{LocalIdentity, LoginUI, CashShop} {
|
||||
path := filepath.Join(gameDir, "BepInEx", "plugins", spec.FileName())
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil || string(data) != spec.FileName() {
|
||||
|
||||
@@ -50,6 +50,7 @@ type ResourcePolicy struct {
|
||||
type InstallResult struct {
|
||||
LocalIdentity plugin.Result `json:"local_identity"`
|
||||
LoginUI plugin.Result `json:"login_ui"`
|
||||
CashShop plugin.Result `json:"cash_shop"`
|
||||
}
|
||||
|
||||
func Inspect(gameDir string, versions clientconfig.ReleaseVersions) (GameStatus, error) {
|
||||
@@ -117,6 +118,7 @@ func InstallPlugins(
|
||||
versions clientconfig.ReleaseVersions,
|
||||
localIdentitySource string,
|
||||
loginUISource string,
|
||||
cashShopSource string,
|
||||
) (InstallResult, error) {
|
||||
status, err := Inspect(gameDir, versions)
|
||||
if err != nil {
|
||||
@@ -136,6 +138,10 @@ func InstallPlugins(
|
||||
if err != nil {
|
||||
return InstallResult{}, err
|
||||
}
|
||||
cashSource, err := plugin.ResolvePackaged(plugin.CashShop, cashShopSource)
|
||||
if err != nil {
|
||||
return InstallResult{}, err
|
||||
}
|
||||
local, err := plugin.Install(plugin.LocalIdentity, gameDir, localSource)
|
||||
if err != nil {
|
||||
return InstallResult{}, err
|
||||
@@ -144,7 +150,11 @@ func InstallPlugins(
|
||||
if err != nil {
|
||||
return InstallResult{}, err
|
||||
}
|
||||
return InstallResult{LocalIdentity: local, LoginUI: login}, nil
|
||||
cash, err := plugin.Install(plugin.CashShop, gameDir, cashSource)
|
||||
if err != nil {
|
||||
return InstallResult{}, err
|
||||
}
|
||||
return InstallResult{LocalIdentity: local, LoginUI: login, CashShop: cash}, nil
|
||||
}
|
||||
|
||||
func FetchResourcePolicy(ctx context.Context, client *http.Client, settings clientconfig.Settings, versions clientconfig.ReleaseVersions) (ResourcePolicy, error) {
|
||||
|
||||
Reference in New Issue
Block a user