diff options
| author | historia <[not public]> | 2026-07-07 00:22:32 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-07 00:22:32 -0400 |
| commit | a51f7a53aa2c487ebf94ba0363038574ae8bb590 (patch) | |
| tree | b393a8f9e4732b40f6fe8058d086bd631537ad3b /internal/game/cmd_swapid.go | |
| parent | 9292634f2f8d71a53879ff07e1330c671b207d51 (diff) | |
| download | thehouseoficarus-a51f7a53aa2c487ebf94ba0363038574ae8bb590.tar.gz | |
feat: hidden exits (and related flags) work with commands that change room ids. exit code simplified and unified between impassable and blocked exits.
Diffstat (limited to 'internal/game/cmd_swapid.go')
| -rw-r--r-- | internal/game/cmd_swapid.go | 91 |
1 files changed, 54 insertions, 37 deletions
diff --git a/internal/game/cmd_swapid.go b/internal/game/cmd_swapid.go index 6ee1118..bbcc79c 100644 --- a/internal/game/cmd_swapid.go +++ b/internal/game/cmd_swapid.go @@ -3,10 +3,10 @@ package game import ( "fmt" "os" - "path/filepath" - "regexp" "strconv" + "gopkg.in/yaml.v3" + "thehouseoficarus/internal/net" ) @@ -63,38 +63,25 @@ func (g *Game) executeSwapID(sess *net.Session, args []string, rawInput string) return } - roomsDir := filepath.Join(g.DataDir, "rooms") - re1 := regexp.MustCompile(fmt.Sprintf(`\b%d\b`, id1)) - re2 := regexp.MustCompile(fmt.Sprintf(`\b%d\b`, id2)) - placeholder := fmt.Sprintf("__SWAPID_TEMP_%d__", id1^id2) - - var modified []string - err := filepath.WalkDir(roomsDir, func(roomPath string, d os.DirEntry, err error) error { - if err != nil || d.IsDir() || filepath.Ext(roomPath) != ".yaml" { - return nil - } - data, rerr := os.ReadFile(roomPath) - if rerr != nil { - return nil - } - text := string(data) - if !re1.MatchString(text) && !re2.MatchString(text) { - return nil + // Refuse while non-admin players are online: swapping room IDs relocates + // every character whose state embeds those IDs, which is disruptive to + // normal play. Admins may stay online (their own state is migrated too). + for _, other := range g.Hub.AllSessions() { + if other == sess || other.Player == nil { + continue } - text = re1.ReplaceAllString(text, placeholder) - text = re2.ReplaceAllString(text, strconv.Itoa(id1)) - text = regexp.MustCompile(placeholder).ReplaceAllString(text, strconv.Itoa(id2)) - if werr := os.WriteFile(roomPath, []byte(text), 0644); werr != nil { - return werr + if !g.checkAdmin(other) { + sess.WriteLine("Cannot swap room IDs while non-admin players are online.") + return } - modified = append(modified, roomPath) - return nil - }) - if err != nil { - sess.WriteLine(fmt.Sprintf("Error updating room files: %v", err)) - return } + // Swap the two files first. After this, path1 holds the old room id2's + // content and path2 holds the old room id1's content. Rebuilding the index + // then makes LoadRoom(id1)/LoadRoom(id2) return the swapped content, so the + // reference rewrite below operates on the correctly-positioned rooms and + // re-marshaling writes an `id:` field matching the new filename (keeping + // the file's id field consistent with its name, as the old regex did). tempPath := path1 + ".swapid-tmp" if rerr := os.Rename(path1, tempPath); rerr != nil { sess.WriteLine(fmt.Sprintf("Error renaming room %d: %v", id1, rerr)) @@ -113,14 +100,44 @@ func (g *Game) executeSwapID(sess *net.Session, args []string, rawInput string) } g.World.RebuildRoomIndex(g.DataDir) - g.World.LoadRoom(id1) - g.World.LoadRoom(id2) - if p.RoomID == id1 { - p.RoomID = id2 - } else if p.RoomID == id2 { - p.RoomID = id1 + // Remap every genuine room-ID reference (exit targets, trigger/on-enter + // room+teleport+despawn_rooms, mob wander_rooms) across all rooms. This + // replaces the old \b<id>\b regex, which also clobbered unrelated bare + // integers (delays, quantities, color indices) and author flag values. + // The two swapped rooms are always re-marshaled to correct their `id:` + // field; other rooms are re-marshaled only if a reference actually moved. + idMap := map[int]int{id1: id2, id2: id1} + var updated int + for id := range g.World.RoomIndex() { + room, err := g.World.LoadRoom(id) + if err != nil { + continue + } + changed := room.RewriteRoomIDs(idMap) + if !changed && id != id1 && id != id2 { + continue + } + path, ok := g.World.GetRoomPath(id) + if !ok { + continue + } + data, merr := yaml.Marshal(room) + if merr != nil { + continue + } + if werr := os.WriteFile(path, data, 0644); werr != nil { + continue + } + updated++ } - sess.WriteLine(fmt.Sprintf("Swapped room IDs %d and %d (%d files updated).", id1, id2, len(modified))) + // Migrate room-ID-embedded state across all characters (online + offline): + // discovered-exit player flags, current RoomID, EnterSeqRoom, MapSymbols, + // and RoomsVisited. This is what the player asked for — without it, + // swapid leaves every character's hidden_exit_<id>_<dir> flags (and more) + // pointing at stale room IDs. + g.RewriteRoomIDs(idMap) + + sess.WriteLine(fmt.Sprintf("Swapped room IDs %d and %d (%d files updated).", id1, id2, updated)) } |
