You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.8 KiB
Go
173 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/goccy/go-graphviz/cgraph"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
func NewMapper(fn string) (AardMapper, error) {
|
|
var am AardMapper
|
|
|
|
db, err := sql.Open("sqlite3", fn)
|
|
if err != nil {
|
|
|
|
return am, fmt.Errorf("error opening mapper sqlite3 database: %s\n", err)
|
|
}
|
|
am.DB = db
|
|
return am, nil
|
|
}
|
|
|
|
type AardMapper struct {
|
|
DB *sql.DB
|
|
RoomCache map[string]Room
|
|
AreaCache map[string]Area
|
|
}
|
|
|
|
func (am AardMapper) Areas() (map[string]Area, error) {
|
|
areas := make(map[string]Area)
|
|
rows, err := am.DB.Query("select uid, name, texture, color, flags from areas;")
|
|
defer rows.Close()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error selecting areas: %w", err)
|
|
}
|
|
for rows.Next() {
|
|
a := Area{}
|
|
err := rows.Scan(&a.Uid, &a.Name, &a.Texture, &a.Color, &a.Flags)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error scanning Areas: %d", err)
|
|
}
|
|
|
|
areas[a.Uid.String] = a
|
|
}
|
|
return areas, nil
|
|
}
|
|
|
|
func (am AardMapper) Area(uid string) (Area, error) {
|
|
a, ok := am.AreaCache[uid]
|
|
if ok {
|
|
return a, nil
|
|
}
|
|
a = Area{}
|
|
row := am.DB.QueryRow("select uid, name, flags, color, texture from areas where uid = :uid")
|
|
|
|
if err := row.Scan(&a.Uid, &a.Name, &a.Flags, &a.Color, &a.Texture); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return a, fmt.Errorf("no area found: %w", err)
|
|
}
|
|
}
|
|
|
|
return a, nil
|
|
}
|
|
|
|
func (am AardMapper) Rooms() ([]Room, error) {
|
|
var uidCount int
|
|
uidCountRow := am.DB.QueryRow("select count(uid) from rooms;")
|
|
uidCountRow.Scan(&uidCount)
|
|
log.Println("uidCount:", uidCount)
|
|
rows, err := am.DB.Query("select uid from rooms;")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error querying room UIDs: %w", err)
|
|
}
|
|
var uids = make([]string, uidCount)
|
|
i := 0
|
|
for rows.Next() {
|
|
var uid string
|
|
if err := rows.Scan(&uid); err != nil {
|
|
return nil, fmt.Errorf("error querying room %q from uid: %w", uid, err)
|
|
}
|
|
uids[i] = uid
|
|
}
|
|
rooms := make([]Room, uidCount)
|
|
for idx, uid := range uids {
|
|
room, err := am.Room(uid)
|
|
if err != nil {
|
|
return rooms, fmt.Errorf("error fetching room: %s", err)
|
|
}
|
|
rooms[idx] = room
|
|
}
|
|
return rooms, nil
|
|
}
|
|
|
|
func (am AardMapper) Room(uid string) (Room, error) {
|
|
r := Room{}
|
|
row := am.DB.QueryRow("select uid,name,building,info,notes,flags,area,norecall,noportal from rooms where uid = ?", uid)
|
|
|
|
if err := row.Scan(&r.Uid, &r.Name, &r.Building, &r.Info, &r.Notes, &r.Flags, &r.AreaName, &r.Norecall, &r.Noportal); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return r, fmt.Errorf("no room found: %w", err)
|
|
}
|
|
log.Println("room found:", r.Uid.String)
|
|
}
|
|
|
|
return r, nil
|
|
}
|
|
|
|
func (am AardMapper) Exits(uid string) ([]RoomExit, error) {
|
|
exits := make([]RoomExit, 0)
|
|
rows, err := am.DB.Query("select r.uid,r.name,r.building,r.info,r.notes,r.flags,r.area, r.norecall,r.noportal,e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid where uid=:uid;", uid)
|
|
// rows, err := am.DB.Query("select e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid;")
|
|
defer rows.Close()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error selecting areas: %w", err)
|
|
}
|
|
for rows.Next() {
|
|
e := RoomExit{}
|
|
// err := rows.Scan(&e.Dir, &e.Fromuid, &e.Touid, &e.Level)
|
|
err := rows.Scan(&e.Uid, &e.Name, &e.Building, &e.Info, &e.Notes, &e.Flags, &e.AreaName, &e.Norecall, &e.Noportal, &e.Dir, &e.Fromuid, &e.Touid, &e.Level)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error scanning Areas: %d", err)
|
|
}
|
|
|
|
exits = append(exits, e)
|
|
}
|
|
return exits, nil
|
|
}
|
|
|
|
func (am AardMapper) Walk(uid string, g *cgraph.Graph) error {
|
|
originRoom, err := am.Room(uid)
|
|
if err != nil {
|
|
return fmt.Errorf("error querying origin room for walk %q: %w", uid, err)
|
|
}
|
|
area, err := am.Area(originRoom.AreaName.String)
|
|
if err != nil {
|
|
return fmt.Errorf("error querying origin area for walk %q: %w", uid, err)
|
|
}
|
|
|
|
sg := g.SubGraph("cluster_"+area.Name.String, 1)
|
|
sg.SetLabel(area.Name.String)
|
|
|
|
originNode, err := sg.CreateNode(originRoom.Uid.String)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating origin node for room %s: %w", originRoom.Name.String, err)
|
|
}
|
|
sg.SetLabel(originRoom.Name.String)
|
|
|
|
exits, err := am.Exits(originRoom.Uid.String)
|
|
if err != nil {
|
|
return fmt.Errorf("error fetching exits for origin room %s: %w", originRoom.Name.String, err)
|
|
}
|
|
|
|
for _, exit := range exits {
|
|
destRoom, err := am.Room(exit.Touid.String)
|
|
if err != nil {
|
|
return fmt.Errorf("error querying destination room %s: %w", uid, err)
|
|
}
|
|
destNode, err := sg.CreateNode(destRoom.Uid.String)
|
|
destNode.SetLabel(destRoom.Name.String)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating destination node for room %s: %w", originRoom.Name.String, err)
|
|
}
|
|
// maybe i'll save recursion for later
|
|
// am.Walk(exit.Touid.String, g) // gotta prevent cycles. check if edge already exists and return?
|
|
sg.CreateEdge(originRoom.Uid.String+">"+originRoom.Uid.String, originNode, destNode)
|
|
}
|
|
|
|
return nil
|
|
}
|