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:
2026-10-05 11:50:31 +08:00
parent eb445f88f4
commit d8771caef2
222 changed files with 19000 additions and 4284 deletions
+2 -1
View File
@@ -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 {
+4 -4
View File
@@ -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() {