123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- package funmow
-
- import (
- "bytes"
- "encoding/binary"
- "encoding/json"
- "errors"
- "fmt"
- "github.com/boltdb/bolt"
- "regexp"
- "strconv"
- )
-
- const (
- DEBUG = false
- )
-
- type DBRef int
-
- func NewDBRefFromHashRef(v string) (DBRef, bool) {
- var destInt int
-
- if matched, _ := regexp.MatchString(`^\pZ*#[0-9]+\pZ*?`, v); !matched {
- return 0, false
- }
-
- n, err := fmt.Sscanf(v, "#%d", &destInt)
-
- if err != nil || n == 0 {
- return 0, false
- }
-
- return DBRef(destInt), true
- }
-
- func NewDBRefFromString(v string) (DBRef, error) {
- intVal, err := strconv.Atoi(v)
- return DBRef(intVal), err
- }
-
- func NewDBRefFromKey(b []byte) DBRef {
- return DBRef(binary.BigEndian.Uint64(b))
- }
-
- func NewDBRefFromChildKey(b []byte) DBRef { // returns the child part of a childkey
- return DBRef(binary.BigEndian.Uint64(b[8:]))
- }
-
- func (r DBRef) Valid() bool {
- return r != 0
- }
-
- func (r DBRef) Limbo() bool {
- return r == 0
- }
-
- func (r DBRef) String() string {
- return strconv.Itoa(int(r))
- }
-
- func (r DBRef) Key() []byte {
- b := make([]byte, 8)
- binary.BigEndian.PutUint64(b, uint64(r))
- return b
- }
-
- func (r DBRef) ChildKey(s DBRef) []byte {
- b := make([]byte, 16)
- binary.BigEndian.PutUint64(b, uint64(r))
- binary.BigEndian.PutUint64(b[8:], uint64(s))
- return b
- }
-
- type DBRefList []DBRef
-
- func (l DBRefList) First() DBRef {
- if len(l) > 0 {
- return l[0]
- } else {
- return 0
- }
- }
-
- type DB struct {
- path string
- db *bolt.DB
- }
-
- func NewDB(path string) *DB {
- return &DB{path: path}
- }
-
- func (s *DB) Close() {
- s.db.Close()
- }
-
- func (s *DB) Open() error {
- var err error
- s.db, err = bolt.Open(s.path, 0600, nil)
- if err != nil {
- return err
- }
- return s.db.Update(func(tx *bolt.Tx) error {
- var err error
- _, err = tx.CreateBucketIfNotExists([]byte("object"))
- if err != nil {
- return fmt.Errorf("create bucket: %s", err)
- }
-
- _, err = tx.CreateBucketIfNotExists([]byte("child"))
- if err != nil {
- return fmt.Errorf("create bucket: %s", err)
- }
-
- _, err = tx.CreateBucketIfNotExists([]byte("parent"))
- if err != nil {
- return fmt.Errorf("create bucket: %s", err)
- }
-
- _, err = tx.CreateBucketIfNotExists([]byte("player"))
- if err != nil {
- return fmt.Errorf("create bucket: %s", err)
- }
-
- return nil
- })
- }
-
- func (s *DB) Allocate() (DBRef, error) {
- var id DBRef
- err := s.db.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("object"))
- seq, err := b.NextSequence()
- if err != nil {
- return err
- }
- id = DBRef(seq)
- return s.txStoreObject(b, Object{ID: id}, id)
- })
- return id, err
- }
-
- func (s *DB) Fetch(r DBRef) (Object, bool) { // this has become simply an alias for RetrieveObject
-
- return s.RetrieveObject(r)
- }
-
- func (s *DB) DumpObject(r DBRef) string {
- var dump string
- s.db.View(func(tx *bolt.Tx) error {
- objectBucket := tx.Bucket([]byte("object"))
- childBucket := tx.Bucket([]byte("child"))
- parentBucket := tx.Bucket([]byte("parent"))
-
- objectDump := string(objectBucket.Get(r.Key()))
-
- intermediate := make([]string, 0)
- children := s.txGetChildren(childBucket, r)
- for childID, childType := range children {
- intermediate = append(intermediate, fmt.Sprintf("#%d (%s)", childID, childType))
- }
-
- parent, hasParent := s.txGetParent(parentBucket, r)
-
- dump = fmt.Sprintf("Object: %s\nHas Parent: %t\nParent ID: #%d\nChildren: %s", objectDump, hasParent, parent, intermediate)
-
- return nil
- })
- return dump
- }
-
- func (s *DB) Delete(objectID DBRef) error {
- return s.db.Update(func(tx *bolt.Tx) error {
-
- objectBucket := tx.Bucket([]byte("object"))
- childBucket := tx.Bucket([]byte("child"))
- parentBucket := tx.Bucket([]byte("parent"))
-
- _, found := s.txRetrieveObject(objectBucket, objectID)
-
- if !found {
- return nil
- }
-
- parentID, parentFound := s.txGetParent(parentBucket, objectID)
-
- if parentFound {
- //fmt.Printf("Unlinking from parent #%d\n", parentID)
- err := s.txUnlink(childBucket, parentBucket, parentID, objectID)
- if err != nil {
- return err
- }
- }
-
- children := s.txGetChildren(childBucket, objectID)
-
- for childID, childType := range children {
- //fmt.Printf("Unlinking child #%d\n",childID)
- err := s.txUnlink(childBucket, parentBucket, objectID, childID)
- if err != nil {
- return err
- }
- if childType == "exit" {
- //fmt.Printf("Deleting exit child #%d\n", childID)
- err := s.txDelete(objectBucket, childID) // this is bad and will create orphans
- if err != nil {
- return err
- }
- } else {
- // even if parentfound == false, parentID will = 0 (the default value for rooms)
- // that will cause the contents of destroyed rooms to end up in limbo, instead of as orpans
- //fmt.Printf("Linking child #%d to parent #%d\n",childID, parentID)
- err := s.txLink(childBucket, parentBucket, parentID, childID, childType)
- if err != nil {
- return err
- }
- }
- }
- //fmt.Printf("Deleting object #%d\n", objectID)
- err := s.txDelete(objectBucket, objectID)
- if err != nil {
- return err
- }
-
- if DEBUG {
- fmt.Printf("Delete name: id: %d\n", objectID)
- }
-
- return nil
- })
- }
-
- func (s *DB) SetPlayerPassword(name string, oldPassword string, newPassword string) bool {
-
- updated := false
-
- s.db.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("player"))
- v := b.Get([]byte(name))
- if v == nil {
- return fmt.Errorf("player doesn't exist")
- }
- var player PlayerMeta
- err := json.Unmarshal(v, &player)
- if err != nil {
- return err
- }
-
- if player.Password != oldPassword {
- return fmt.Errorf("password mismatch")
- }
-
- player.Password = newPassword
-
- buf, err := json.Marshal(player)
- if err != nil {
- return err
- }
- err = b.Put([]byte(name), buf)
- if err != nil {
- return err
- }
- updated = true
- return nil
- })
-
- return updated
- }
-
- func (s *DB) SetPlayer(name string, player PlayerMeta) error {
-
- err := s.db.Update(func(tx *bolt.Tx) error {
-
- buf, err := json.Marshal(player)
-
- if err != nil {
- return err
- }
-
- b := tx.Bucket([]byte("player"))
-
- return b.Put([]byte(name), buf)
- })
-
- return err
-
- }
-
- func (s *DB) GetPlayer(name string) (PlayerMeta, bool) {
- var player PlayerMeta
- found := false
-
- s.db.View(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("player"))
- v := b.Get([]byte(name))
- if v == nil {
- return nil
- }
- err := json.Unmarshal(v, &player)
- if err == nil {
- found = true
- }
- return nil
- })
-
- return player, found
- }
-
- func (s *DB) DeletePlayer(name string) error {
- return s.db.Update(func(tx *bolt.Tx) error {
- playerBucket := tx.Bucket([]byte("player"))
- err := playerBucket.Delete([]byte(name))
- return err
- })
- }
-
- func (s *DB) RenamePlayer(oldName string, newName string) error {
- // doing it this way accomplishes the change as a single transasction
- // and saves a bunch of marshalling and unmarshalling
- return s.db.Update(func(tx *bolt.Tx) error {
- playerBucket := tx.Bucket([]byte("player"))
- buf := playerBucket.Get([]byte(oldName))
- if buf == nil {
- return errors.New("Can't find player")
- }
- err := playerBucket.Delete([]byte(oldName))
- if err != nil {
- return err
- }
- return playerBucket.Put([]byte(newName), buf)
- })
- }
-
- func (s *DB) CreatePlayer(name string, password string, flags map[string]bool) (DBRef, error) {
-
- var playerID DBRef
-
- err := s.db.Update(func(tx *bolt.Tx) error {
- objectBucket := tx.Bucket([]byte("object"))
- playerBucket := tx.Bucket([]byte("player"))
- seq, err := objectBucket.NextSequence()
- if err != nil {
- return err
- }
- playerID = DBRef(seq)
- s.txStoreObject(objectBucket, Object{ID: playerID, Name: name, Type: "player", Flags: flags}, playerID)
- playerMeta := PlayerMeta{ID: playerID, Password: password}
- buf, err := json.Marshal(playerMeta)
- if err != nil {
- return err
- }
- err = playerBucket.Put([]byte(name), buf)
- return err
- })
-
- if DEBUG {
- fmt.Printf("CreatePlayer name: %s id: %d\n", name, playerID)
- }
-
- return playerID, err
- }
-
- // All of these functions exist in two forms; one for use inside a transaction and one
- // for use outside a transaction.
-
- func (s *DB) RetrieveObject(id DBRef) (Object, bool) {
-
- o := Object{}
- f := false
- s.db.View(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("object"))
- o, f = s.txRetrieveObject(b, id)
- return nil
- })
- return o, f
-
- }
-
- func (s *DB) txRetrieveObject(b *bolt.Bucket, id DBRef) (Object, bool) {
-
- o := Object{}
- f := false
- v := b.Get(id.Key())
- if v != nil {
- err := json.Unmarshal(v, &o)
- if err == nil {
- f = true
-
- if DEBUG {
- fmt.Printf("txRetrieveObject id: %d\n", id)
- }
-
- }
- }
- o.db = s // bit ugly but saves a lot of headaches
-
- return o, f
-
- }
-
- func (s *DB) StoreObject(o Object, id DBRef) error {
-
- return s.db.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("object"))
- return s.txStoreObject(b, o, id)
- })
-
- }
-
- func (s *DB) txStoreObject(b *bolt.Bucket, o Object, id DBRef) error {
-
- buf, err := json.Marshal(o)
-
- if err != nil {
- return err
- }
- err = b.Put(id.Key(), buf)
- if err != nil {
- return err
- }
-
- if DEBUG {
- fmt.Printf("txStoreObject id: %d\n", id)
- }
-
- return nil
-
- }
-
- func (s *DB) GetChildren(src DBRef) map[DBRef]string {
-
- var l map[DBRef]string
- s.db.View(func(tx *bolt.Tx) error {
- c := tx.Bucket([]byte("child"))
- l = s.txGetChildren(c, src)
- return nil
- })
- return l
-
- }
-
- func (s *DB) txGetChildren(b *bolt.Bucket, src DBRef) map[DBRef]string {
-
- l := make(map[DBRef]string)
- prefix := src.Key() // 00001234
- start := src.ChildKey(0) // 0000123400000000
- c := b.Cursor()
- for k, v := c.Seek(start); bytes.HasPrefix(k, prefix); k, v = c.Next() {
- l[NewDBRefFromChildKey(k)] = string(v)
- }
- return l
-
- }
-
- func (s *DB) GetParent(src DBRef) (DBRef, bool) {
-
- p := DBRef(0)
- f := false
-
- s.db.View(func(tx *bolt.Tx) error {
- parentBucket := tx.Bucket([]byte("parent"))
- p, f = s.txGetParent(parentBucket, src)
- return nil
- })
- return p, f
-
- }
-
- func (s *DB) txGetParent(parentBucket *bolt.Bucket, childID DBRef) (DBRef, bool) {
-
- parentID := DBRef(0)
- f := false
-
- v := parentBucket.Get(childID.Key())
- if v != nil {
- parentID = NewDBRefFromKey(v)
- f = true
- }
-
- if DEBUG {
- fmt.Printf("txGetParent child: %d parent: %d\n", childID, parentID)
- }
-
- //fmt.Printf("txGetParent src: %d found: %t parent: %d\n", src, f, p)
- return parentID, f
-
- }
-
- func (s *DB) IsParent(src DBRef, dest DBRef) bool {
-
- f := false
- s.db.View(func(tx *bolt.Tx) error {
- parentBucket := tx.Bucket([]byte("parent"))
- f = s.txIsParent(parentBucket, src, dest)
- return nil
- })
- return f
-
- }
-
- func (s *DB) txIsParent(b *bolt.Bucket, src DBRef, dst DBRef) bool {
-
- v := b.Get(src.Key())
- if v != nil {
- return (bytes.Compare(v, dst.Key()) == 0)
- }
- return false
-
- }
-
- func (s *DB) IsChild(src DBRef, dest DBRef) (string, bool) {
-
- t := ""
- f := false
- s.db.View(func(tx *bolt.Tx) error {
- childBucket := tx.Bucket([]byte("child"))
- t, f = s.txIsChild(childBucket, src, dest)
- return nil
- })
- return t, f
-
- }
-
- func (s *DB) txIsChild(b *bolt.Bucket, src DBRef, dest DBRef) (string, bool) {
-
- v := b.Get(src.ChildKey(dest))
- if v != nil {
- return string(v), true
- }
- return "", false
-
- }
-
- func (s *DB) Link(parentID DBRef, childID DBRef, linkType string) error {
-
- return s.db.Update(func(tx *bolt.Tx) error {
- childBucket := tx.Bucket([]byte("child"))
- parentBucket := tx.Bucket([]byte("parent"))
-
- return s.txLink(childBucket, parentBucket, parentID, childID, linkType)
- })
-
- }
-
- func (s *DB) txLink(childBucket *bolt.Bucket, parentBucket *bolt.Bucket, parentID DBRef, childID DBRef, linkType string) error {
-
- //fmt.Printf("txLink attempting to put %d inside %d\n", childID, parentID)
-
- prevParentID, found := s.txGetParent(parentBucket, childID)
- if found {
- //fmt.Printf("txLink removing %d from previous parent %d\n", childID, prevParentID)
- err := s.txUnlink(childBucket, parentBucket, prevParentID, childID)
- if err != nil {
- return err
- }
- }
-
- err := childBucket.Put(parentID.ChildKey(childID), []byte(linkType))
- if err != nil {
- return err
- }
-
- err = parentBucket.Put(childID.Key(), parentID.Key())
- if err != nil {
- return err
- }
-
- if DEBUG {
- fmt.Printf("txLink parent: %d child: %d\n", parentID, childID)
- }
-
- return nil
-
- }
-
- func (s *DB) Unlink(src DBRef, dest DBRef) error {
-
- return s.db.Update(func(tx *bolt.Tx) error {
- childBucket := tx.Bucket([]byte("child"))
- parentBucket := tx.Bucket([]byte("parent"))
- return s.txUnlink(childBucket, parentBucket, src, dest)
- })
- }
-
- func (s *DB) txUnlink(childBucket *bolt.Bucket, parentBucket *bolt.Bucket, parentID DBRef, childID DBRef) error {
-
- err := childBucket.Delete(parentID.ChildKey(childID))
- if err != nil {
- return err
- }
-
- err = parentBucket.Delete(childID.Key())
- if err != nil {
- return err
- }
-
- if DEBUG {
- fmt.Printf("txUnlink parent: %d child: %d\n", parentID, childID)
- }
-
- return nil
-
- }
-
- func (s *DB) txDelete(b *bolt.Bucket, id DBRef) error {
- if DEBUG {
- fmt.Printf("txDelete %d\n", id)
- }
-
- return b.Delete(id.Key())
- }
|