From eafd3b4879dfc3173d311185e624a92c7d1d2061 Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Wed, 31 Dec 2025 00:03:13 +0900 Subject: [PATCH 1/7] Add protagonists field to config and query string --- config.yml.example | 5 ++++ server/config.go | 14 ++++++++--- server/locations.go | 60 ++++++++++++++++++++++++++++++++------------- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/config.yml.example b/config.yml.example index fc5f0b8..bb4c4e8 100644 --- a/config.yml.example +++ b/config.yml.example @@ -4,6 +4,11 @@ ## Path to game files #game_path: "" +## Protagonists for games with multiple protagonists (comma-separated) +## Leave empty or omit for single protagonist games +## Example: "kubotsuki,totsutsuki" for Uneven Dream +#protagonists: "" + ## Database user #db_user: "" diff --git a/server/config.go b/server/config.go index a8119b3..1777ca8 100644 --- a/server/config.go +++ b/server/config.go @@ -27,8 +27,9 @@ import ( ) type Config struct { - gameName string - gamePath string + gameName string + gamePath string + protagonists []string dbUser, dbPass, dbAddr, dbName string @@ -68,8 +69,9 @@ type Config struct { } type ConfigFile struct { - GameName string `yaml:"game_name"` - GamePath string `yaml:"game_path"` + GameName string `yaml:"game_name"` + GamePath string `yaml:"game_path"` + Protagonists string `yaml:"protagonists"` DbUser string `yaml:"db_user"` DbPass string `yaml:"db_pass"` @@ -129,6 +131,10 @@ func parseConfigFile(filename string) *Config { config.gameName = configFile.GameName config.gamePath = configFile.GamePath + if configFile.Protagonists != "" { + config.protagonists = strings.Split(configFile.Protagonists, ",") + } + config.dbUser = configFile.DbUser config.dbPass = configFile.DbPass config.dbAddr = configFile.DbAddr diff --git a/server/locations.go b/server/locations.go index 3a73dff..63952e2 100644 --- a/server/locations.go +++ b/server/locations.go @@ -131,26 +131,52 @@ func updateLocationCache() { var locations []*Location var wikiLocations []LocationResponse var locationsResponse LocationsResponse - continueKey := "0" - for continueKey != "" { - response, err := queryWiki("locations", fmt.Sprintf("continueKey=%s", continueKey)) - if err != nil { - writeErrLog("SERVER", "Locations", err.Error()) - return + // if multiple protagonists, get locations for each protagonist + if len(config.protagonists) > 0 { + for _, protag := range config.protagonists { + continueKey := "0" + for continueKey != "" { + queryStr := fmt.Sprintf("protag=%s&continueKey=%s", protag, continueKey) + response, err := queryWiki("locations", queryStr) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + err = json.Unmarshal([]byte(response), &locationsResponse) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + wikiLocations = append(wikiLocations, locationsResponse.Locations...) + + continueKey = locationsResponse.ContinueKey + locationsResponse.ContinueKey = "" + } } - - err = json.Unmarshal([]byte(response), &locationsResponse) - if err != nil { - writeErrLog("SERVER", "Locations", err.Error()) - return + } else { + continueKey := "0" + for continueKey != "" { + response, err := queryWiki("locations", fmt.Sprintf("continueKey=%s", continueKey)) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + err = json.Unmarshal([]byte(response), &locationsResponse) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + wikiLocations = append(wikiLocations, locationsResponse.Locations...) + + continueKey = locationsResponse.ContinueKey + + locationsResponse.ContinueKey = "" } - - wikiLocations = append(wikiLocations, locationsResponse.Locations...) - - continueKey = locationsResponse.ContinueKey - - locationsResponse.ContinueKey = "" } locationsMap := make(map[string]*Location) From 708d6462c1cfb955ece00f2eb3cc3ebad2421104 Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Wed, 31 Dec 2025 01:34:02 +0900 Subject: [PATCH 2/7] Minor change --- config.yml.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.yml.example b/config.yml.example index bb4c4e8..637ccbf 100644 --- a/config.yml.example +++ b/config.yml.example @@ -6,7 +6,7 @@ ## Protagonists for games with multiple protagonists (comma-separated) ## Leave empty or omit for single protagonist games -## Example: "kubotsuki,totsutsuki" for Uneven Dream +## Example: "kubotsuki,totsutsuki" for "unevendream" #protagonists: "" ## Database user From e89d9f4d67d483972733ace17a3587e47e441d36 Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Wed, 31 Dec 2025 02:36:18 +0900 Subject: [PATCH 3/7] Rename protagonists to protagNames --- config.yml.example | 4 ++-- server/config.go | 16 ++++++++-------- server/locations.go | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/config.yml.example b/config.yml.example index 637ccbf..6ac20dd 100644 --- a/config.yml.example +++ b/config.yml.example @@ -4,10 +4,10 @@ ## Path to game files #game_path: "" -## Protagonists for games with multiple protagonists (comma-separated) +## Protagonist names for games with multiple protagonists (comma-separated) ## Leave empty or omit for single protagonist games ## Example: "kubotsuki,totsutsuki" for "unevendream" -#protagonists: "" +#protag_names: "" ## Database user #db_user: "" diff --git a/server/config.go b/server/config.go index 1777ca8..1a18ac8 100644 --- a/server/config.go +++ b/server/config.go @@ -27,9 +27,9 @@ import ( ) type Config struct { - gameName string - gamePath string - protagonists []string + gameName string + gamePath string + protagNames []string dbUser, dbPass, dbAddr, dbName string @@ -69,9 +69,9 @@ type Config struct { } type ConfigFile struct { - GameName string `yaml:"game_name"` - GamePath string `yaml:"game_path"` - Protagonists string `yaml:"protagonists"` + GameName string `yaml:"game_name"` + GamePath string `yaml:"game_path"` + ProtagNames string `yaml:"protag_names"` DbUser string `yaml:"db_user"` DbPass string `yaml:"db_pass"` @@ -131,8 +131,8 @@ func parseConfigFile(filename string) *Config { config.gameName = configFile.GameName config.gamePath = configFile.GamePath - if configFile.Protagonists != "" { - config.protagonists = strings.Split(configFile.Protagonists, ",") + if configFile.ProtagNames != "" { + config.protagNames = strings.Split(configFile.ProtagNames, ",") } config.dbUser = configFile.DbUser diff --git a/server/locations.go b/server/locations.go index 63952e2..9a08277 100644 --- a/server/locations.go +++ b/server/locations.go @@ -133,8 +133,8 @@ func updateLocationCache() { var locationsResponse LocationsResponse // if multiple protagonists, get locations for each protagonist - if len(config.protagonists) > 0 { - for _, protag := range config.protagonists { + if len(config.protagNames) > 0 { + for _, protag := range config.protagNames { continueKey := "0" for continueKey != "" { queryStr := fmt.Sprintf("protag=%s&continueKey=%s", protag, continueKey) From d68119e089327f5a358594eecf386b3a212ccfb2 Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:44:34 +0900 Subject: [PATCH 4/7] Add logic to merge common locations when there are multiple protagonists --- server/locations.go | 59 ++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/server/locations.go b/server/locations.go index 9a08277..d911d2c 100644 --- a/server/locations.go +++ b/server/locations.go @@ -60,6 +60,7 @@ type LocationResponse struct { VersionsUpdated []string `json:"versionsUpdated"` VersionRemoved string `json:"versionRemoved,omitempty"` VersionGaps []string `json:"versionGaps"` + Protags []string `json:"protags,omitempty"` } type LocationsResponse struct { @@ -80,6 +81,7 @@ type Location struct { VersionAdded string `json:"versionAdded"` VersionsUpdated []string `json:"versionsUpdated"` Secret bool `json:"secret"` + Protags []string `json:"protags,omitempty"` } var locationCache []*Location @@ -129,37 +131,25 @@ func getNext2kkiLocations(originLocationName string, destLocationName string) (P func updateLocationCache() { var locations []*Location - var wikiLocations []LocationResponse var locationsResponse LocationsResponse - // if multiple protagonists, get locations for each protagonist - if len(config.protagNames) > 0 { - for _, protag := range config.protagNames { - continueKey := "0" - for continueKey != "" { - queryStr := fmt.Sprintf("protag=%s&continueKey=%s", protag, continueKey) - response, err := queryWiki("locations", queryStr) - if err != nil { - writeErrLog("SERVER", "Locations", err.Error()) - return - } - - err = json.Unmarshal([]byte(response), &locationsResponse) - if err != nil { - writeErrLog("SERVER", "Locations", err.Error()) - return - } - - wikiLocations = append(wikiLocations, locationsResponse.Locations...) + protags := config.protagNames + if len(protags) == 0 { + protags = []string{""} + } - continueKey = locationsResponse.ContinueKey - locationsResponse.ContinueKey = "" - } - } - } else { + wikiLocationsMap := make(map[string]*LocationResponse) + for _, protag := range protags { continueKey := "0" for continueKey != "" { - response, err := queryWiki("locations", fmt.Sprintf("continueKey=%s", continueKey)) + queryStr := "" + if protag != "" { + queryStr = fmt.Sprintf("protag=%s&continueKey=%s", protag, continueKey) + } else { + queryStr = fmt.Sprintf("continueKey=%s", continueKey) + } + + response, err := queryWiki("locations", queryStr) if err != nil { writeErrLog("SERVER", "Locations", err.Error()) return @@ -171,14 +161,26 @@ func updateLocationCache() { return } - wikiLocations = append(wikiLocations, locationsResponse.Locations...) + // Merge locations for duplicate locations if multiple protagonists + for i := range locationsResponse.Locations { + wikiLoc := &locationsResponse.Locations[i] + if existing, ok := wikiLocationsMap[wikiLoc.Title]; ok { + existing.Protags = append(existing.Protags, wikiLoc.Protags...) + } else { + wikiLocationsMap[wikiLoc.Title] = wikiLoc + } + } continueKey = locationsResponse.ContinueKey - locationsResponse.ContinueKey = "" } } + wikiLocations := make([]LocationResponse, 0, len(wikiLocationsMap)) + for _, loc := range wikiLocationsMap { + wikiLocations = append(wikiLocations, *loc) + } + locationsMap := make(map[string]*Location) results, err := db.Query("SELECT id, title, depth, minDepth, secret FROM gameLocations WHERE game = ?", config.gameName) @@ -208,6 +210,7 @@ func updateLocationCache() { location.ContributingAuthors = wikiLocation.ContributingAuthors location.VersionAdded = wikiLocation.VersionAdded location.VersionsUpdated = wikiLocation.VersionsUpdated + location.Protags = wikiLocation.Protags locations = append(locations, location) } From e07900d7caf3610476e317fc1c5c40ba847e78fc Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Fri, 2 Jan 2026 17:43:47 +0900 Subject: [PATCH 5/7] Delete protags setting to config --- config.yml.example | 5 ----- server/config.go | 14 ++++---------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/config.yml.example b/config.yml.example index 6ac20dd..fc5f0b8 100644 --- a/config.yml.example +++ b/config.yml.example @@ -4,11 +4,6 @@ ## Path to game files #game_path: "" -## Protagonist names for games with multiple protagonists (comma-separated) -## Leave empty or omit for single protagonist games -## Example: "kubotsuki,totsutsuki" for "unevendream" -#protag_names: "" - ## Database user #db_user: "" diff --git a/server/config.go b/server/config.go index 1a18ac8..a8119b3 100644 --- a/server/config.go +++ b/server/config.go @@ -27,9 +27,8 @@ import ( ) type Config struct { - gameName string - gamePath string - protagNames []string + gameName string + gamePath string dbUser, dbPass, dbAddr, dbName string @@ -69,9 +68,8 @@ type Config struct { } type ConfigFile struct { - GameName string `yaml:"game_name"` - GamePath string `yaml:"game_path"` - ProtagNames string `yaml:"protag_names"` + GameName string `yaml:"game_name"` + GamePath string `yaml:"game_path"` DbUser string `yaml:"db_user"` DbPass string `yaml:"db_pass"` @@ -131,10 +129,6 @@ func parseConfigFile(filename string) *Config { config.gameName = configFile.GameName config.gamePath = configFile.GamePath - if configFile.ProtagNames != "" { - config.protagNames = strings.Split(configFile.ProtagNames, ",") - } - config.dbUser = configFile.DbUser config.dbPass = configFile.DbPass config.dbAddr = configFile.DbAddr From fce125f2cf3dbb44d354efdcb31b660ba91db783 Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Fri, 2 Jan 2026 19:11:59 +0900 Subject: [PATCH 6/7] Add protags field to LocationResponce --- server/locations.go | 79 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/server/locations.go b/server/locations.go index d911d2c..b32426f 100644 --- a/server/locations.go +++ b/server/locations.go @@ -22,6 +22,7 @@ import ( "fmt" "net/http" "net/url" + "slices" ) type GameLocation struct { @@ -66,6 +67,7 @@ type LocationResponse struct { type LocationsResponse struct { Locations []LocationResponse `json:"locations"` Game string `json:"game"` + Protags []string `json:"protags,omitempty"` ContinueKey string `json:"continueKey,omitempty"` } @@ -133,23 +135,31 @@ func updateLocationCache() { var locations []*Location var locationsResponse LocationsResponse - protags := config.protagNames - if len(protags) == 0 { - protags = []string{""} + response, err := queryWiki("locations", "continueKey=0") + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return } + err = json.Unmarshal([]byte(response), &locationsResponse) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + protags := locationsResponse.Protags + wikiLocationsMap := make(map[string]*LocationResponse) - for _, protag := range protags { - continueKey := "0" - for continueKey != "" { - queryStr := "" - if protag != "" { - queryStr = fmt.Sprintf("protag=%s&continueKey=%s", protag, continueKey) - } else { - queryStr = fmt.Sprintf("continueKey=%s", continueKey) - } - response, err := queryWiki("locations", queryStr) + if len(protags) == 0 { + continueKey := locationsResponse.ContinueKey + for i := range locationsResponse.Locations { + wikiLoc := &locationsResponse.Locations[i] + wikiLocationsMap[wikiLoc.Title] = wikiLoc + } + + for continueKey != "" { + response, err := queryWiki("locations", fmt.Sprintf("continueKey=%s", continueKey)) if err != nil { writeErrLog("SERVER", "Locations", err.Error()) return @@ -161,19 +171,50 @@ func updateLocationCache() { return } - // Merge locations for duplicate locations if multiple protagonists for i := range locationsResponse.Locations { wikiLoc := &locationsResponse.Locations[i] - if existing, ok := wikiLocationsMap[wikiLoc.Title]; ok { - existing.Protags = append(existing.Protags, wikiLoc.Protags...) - } else { - wikiLocationsMap[wikiLoc.Title] = wikiLoc - } + wikiLocationsMap[wikiLoc.Title] = wikiLoc } continueKey = locationsResponse.ContinueKey locationsResponse.ContinueKey = "" } + } else { + for _, protag := range protags { + continueKey := "0" + for continueKey != "" { + queryStr := fmt.Sprintf("protag=%s&continueKey=%s", protag, continueKey) + + response, err := queryWiki("locations", queryStr) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + err = json.Unmarshal([]byte(response), &locationsResponse) + if err != nil { + writeErrLog("SERVER", "Locations", err.Error()) + return + } + + // Merge locations for duplicate locations if multiple protagonists + for i := range locationsResponse.Locations { + wikiLoc := &locationsResponse.Locations[i] + if existing, ok := wikiLocationsMap[wikiLoc.Title]; ok { + for _, p := range wikiLoc.Protags { + if !slices.Contains(existing.Protags, p) { + existing.Protags = append(existing.Protags, p) + } + } + } else { + wikiLocationsMap[wikiLoc.Title] = wikiLoc + } + } + + continueKey = locationsResponse.ContinueKey + locationsResponse.ContinueKey = "" + } + } } wikiLocations := make([]LocationResponse, 0, len(wikiLocationsMap)) From 8cd1b00a04a699c71a86e132a1a99fffb50feb87 Mon Sep 17 00:00:00 2001 From: zebraed <30438415+zebraed@users.noreply.github.com> Date: Fri, 6 Feb 2026 11:47:33 +0900 Subject: [PATCH 7/7] Ensure pointer safety, sort by Title --- server/locations.go | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/server/locations.go b/server/locations.go index b32426f..e4bf312 100644 --- a/server/locations.go +++ b/server/locations.go @@ -23,6 +23,7 @@ import ( "net/http" "net/url" "slices" + "sort" ) type GameLocation struct { @@ -151,13 +152,14 @@ func updateLocationCache() { wikiLocationsMap := make(map[string]*LocationResponse) + for i := range locationsResponse.Locations { + loc := new(LocationResponse) + *loc = locationsResponse.Locations[i] + wikiLocationsMap[loc.Title] = loc + } + if len(protags) == 0 { continueKey := locationsResponse.ContinueKey - for i := range locationsResponse.Locations { - wikiLoc := &locationsResponse.Locations[i] - wikiLocationsMap[wikiLoc.Title] = wikiLoc - } - for continueKey != "" { response, err := queryWiki("locations", fmt.Sprintf("continueKey=%s", continueKey)) if err != nil { @@ -172,8 +174,9 @@ func updateLocationCache() { } for i := range locationsResponse.Locations { - wikiLoc := &locationsResponse.Locations[i] - wikiLocationsMap[wikiLoc.Title] = wikiLoc + loc := new(LocationResponse) + *loc = locationsResponse.Locations[i] + wikiLocationsMap[loc.Title] = loc } continueKey = locationsResponse.ContinueKey @@ -199,15 +202,16 @@ func updateLocationCache() { // Merge locations for duplicate locations if multiple protagonists for i := range locationsResponse.Locations { - wikiLoc := &locationsResponse.Locations[i] - if existing, ok := wikiLocationsMap[wikiLoc.Title]; ok { - for _, p := range wikiLoc.Protags { + loc := new(LocationResponse) + *loc = locationsResponse.Locations[i] + if existing, ok := wikiLocationsMap[loc.Title]; ok { + for _, p := range loc.Protags { if !slices.Contains(existing.Protags, p) { existing.Protags = append(existing.Protags, p) } } } else { - wikiLocationsMap[wikiLoc.Title] = wikiLoc + wikiLocationsMap[loc.Title] = loc } } @@ -217,9 +221,15 @@ func updateLocationCache() { } } + titles := make([]string, 0, len(wikiLocationsMap)) + for title := range wikiLocationsMap { + titles = append(titles, title) + } + sort.Strings(titles) + wikiLocations := make([]LocationResponse, 0, len(wikiLocationsMap)) - for _, loc := range wikiLocationsMap { - wikiLocations = append(wikiLocations, *loc) + for _, title := range titles { + wikiLocations = append(wikiLocations, *wikiLocationsMap[title]) } locationsMap := make(map[string]*Location) @@ -239,8 +249,8 @@ func updateLocationCache() { writeErrLog("SERVER", "Locations", err.Error()) return } - - locationsMap[location.Title] = location + locCopy := &Location{Id: location.Id, Title: location.Title, Depth: location.Depth, MinDepth: location.MinDepth, Secret: location.Secret} + locationsMap[location.Title] = locCopy } for _, wikiLocation := range wikiLocations {