diff options
Diffstat (limited to 'internal/admin/api_rooms.go')
| -rw-r--r-- | internal/admin/api_rooms.go | 192 |
1 files changed, 147 insertions, 45 deletions
diff --git a/internal/admin/api_rooms.go b/internal/admin/api_rooms.go index 985eb49..4075534 100644 --- a/internal/admin/api_rooms.go +++ b/internal/admin/api_rooms.go @@ -269,68 +269,164 @@ func (s *AdminServer) handleRoomDirs(w http.ResponseWriter, r *http.Request) { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } - entries, err := os.ReadDir(filepath.Join(s.dataDir, "rooms")) + base := filepath.Join(s.dataDir, "rooms") + entries, err := os.ReadDir(base) if err != nil { writeJSON(w, []string{""}) return } dirs := []string{""} for _, e := range entries { - if e.IsDir() { + if !e.IsDir() { + continue + } + if hasYAMLFiles(filepath.Join(base, e.Name())) { dirs = append(dirs, e.Name()) } + subEntries, err := os.ReadDir(filepath.Join(base, e.Name())) + if err != nil { + continue + } + for _, se := range subEntries { + if se.IsDir() && hasYAMLFiles(filepath.Join(base, e.Name(), se.Name())) { + dirs = append(dirs, e.Name()+"/"+se.Name()) + } + } } writeJSON(w, dirs) } +func hasYAMLFiles(dir string) bool { + entries, err := os.ReadDir(dir) + if err != nil { + return false + } + for _, e := range entries { + if !e.IsDir() && filepath.Ext(e.Name()) == ".yaml" { + return true + } + } + return false +} + func (s *AdminServer) handleRoomLink(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: var body struct { - From int `json:"from"` - To int `json:"to"` + From int `json:"from"` + To int `json:"to"` + Dir string `json:"dir"` } if err := readJSON(r, &body); err != nil || body.From <= 0 || body.To <= 0 { writeJSON(w, map[string]any{"error": "invalid body, need from and to"}) return } - grid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) { - room, err := s.world.LoadRoom(id) - if err != nil { - return nil, false - } - return room, true - }, nil, nil) - cA, okA := grid.Coord[body.From] - cB, okB := grid.Coord[body.To] - if !okA || !okB || cA[2] != cB[2] { - writeJSON(w, map[string]any{"error": "rooms must be on same z-level"}) + roomA, errA := s.world.LoadRoom(body.From) + roomB, errB := s.world.LoadRoom(body.To) + if errA != nil || errB != nil { + writeJSON(w, map[string]any{"error": "one or both rooms not found"}) return } - dx, dy := cB[0]-cA[0], cB[1]-cA[1] + var dir, oppDir world.ExitDir - for d, delta := range world.DirectionDeltas3D { - if delta[0] == dx && delta[1] == dy && delta[2] == 0 { - dir = d - oppDir = world.OppositeExit[d] - break + if body.Dir != "" { + dir = world.ExitDir(strings.ToLower(body.Dir)) + if opp, ok := world.OppositeExit[dir]; ok { + oppDir = opp + } else { + writeJSON(w, map[string]any{"error": "invalid direction: " + body.Dir}) + return + } + } else { + grid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) { + room, err := s.world.LoadRoom(id) + if err != nil { + return nil, false + } + return room, true + }, nil, nil) + + cA, okA := grid.Coord[body.From] + cB, okB := grid.Coord[body.To] + if !okA || !okB || cA[2] != cB[2] { + writeJSON(w, map[string]any{"error": "rooms must be on same z-level"}) + return + } + dx, dy := cB[0]-cA[0], cB[1]-cA[1] + for d, delta := range world.DirectionDeltas3D { + if delta[0] == dx && delta[1] == dy && delta[2] == 0 { + dir = d + oppDir = world.OppositeExit[d] + break + } + } + if dir == "" { + writeJSON(w, map[string]any{"error": "rooms are not adjacent"}) + return + } + } + + if roomA.Exits != nil { + for d, exit := range roomA.Exits { + if exit.Room == body.To { + delete(roomA.Exits, d) + break + } + } + } + if roomB.Exits != nil { + for d, exit := range roomB.Exits { + if exit.Room == body.From { + delete(roomB.Exits, d) + break + } } } - if dir == "" { - writeJSON(w, map[string]any{"error": "rooms are not adjacent"}) + + conflicts := 0 + testGrid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) { + room, err := s.world.LoadRoom(id) + if err != nil { + return nil, false + } + roomCopy := *room + if room.Exits != nil { + roomCopy.Exits = make(map[world.ExitDir]world.ExitDef, len(room.Exits)) + for k, v := range room.Exits { + if (id == body.From && v.Room == body.To) || (id == body.To && v.Room == body.From) { + continue + } + roomCopy.Exits[k] = v + } + } else { + roomCopy.Exits = make(map[world.ExitDir]world.ExitDef) + } + if id == body.From { + roomCopy.Exits[dir] = world.ExitDef{Room: body.To} + } + if id == body.To { + roomCopy.Exits[oppDir] = world.ExitDef{Room: body.From} + } + return &roomCopy, true + }, nil, func(gc world.GridConflict) { + conflicts++ + }) + _ = testGrid + + if conflicts > 0 { + writeJSONError(w, "link would cause map conflicts (twists or overlaps)", http.StatusConflict) return } - roomA, _ := s.world.LoadRoom(body.From) - roomB, _ := s.world.LoadRoom(body.To) if roomA.Exits == nil { roomA.Exits = make(map[world.ExitDir]world.ExitDef) } + roomA.Exits[dir] = world.ExitDef{Room: body.To} + if roomB.Exits == nil { roomB.Exits = make(map[world.ExitDir]world.ExitDef) } - roomA.Exits[dir] = world.ExitDef{Room: body.To} roomB.Exits[oppDir] = world.ExitDef{Room: body.From} pathA, okA2 := s.world.GetRoomPath(body.From) @@ -378,36 +474,42 @@ func (s *AdminServer) handleRoomLink(w http.ResponseWriter, r *http.Request) { writeJSON(w, map[string]any{"error": "invalid body"}) return } - grid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) { - room, err := s.world.LoadRoom(id) - if err != nil { - return nil, false - } - return room, true - }, nil, nil) - cA, okA := grid.Coord[body.From] - cB, okB := grid.Coord[body.To] - if !okA || !okB || cA[2] != cB[2] { - writeJSON(w, map[string]any{"error": "rooms must be on same z-level"}) + roomA, errA := s.world.LoadRoom(body.From) + roomB, errB := s.world.LoadRoom(body.To) + if errA != nil || errB != nil { + writeJSON(w, map[string]any{"error": "one or both rooms not found"}) return } - dx, dy := cB[0]-cA[0], cB[1]-cA[1] + if roomA == nil || roomB == nil { + writeJSON(w, map[string]any{"error": "one or both rooms not found"}) + return + } + var dir, oppDir world.ExitDir - for d, delta := range world.DirectionDeltas3D { - if delta[0] == dx && delta[1] == dy && delta[2] == 0 { + for _, d := range world.ExitOrder { + if exit, ok := roomA.Exits[d]; ok && exit.Room == body.To { dir = d - oppDir = world.OppositeExit[d] break } } - if dir == "" { - writeJSON(w, map[string]any{"error": "rooms are not adjacent"}) + if dir != "" { + oppDir = world.OppositeExit[dir] + } else { + for _, d := range world.ExitOrder { + if exit, ok := roomB.Exits[d]; ok && exit.Room == body.From { + oppDir = d + dir = world.OppositeExit[d] + break + } + } + } + + if dir == "" && oppDir == "" { + writeJSONError(w, "no exit found between these rooms", http.StatusNotFound) return } - roomA, _ := s.world.LoadRoom(body.From) - roomB, _ := s.world.LoadRoom(body.To) if roomA.Exits != nil { delete(roomA.Exits, dir) } |
