github.com/df-mc/dragonfly@v0.9.13/server/world/mcdb/conf.go (about) 1 package mcdb 2 3 import ( 4 "fmt" 5 "github.com/df-mc/dragonfly/server/entity" 6 "github.com/df-mc/dragonfly/server/world" 7 "github.com/df-mc/dragonfly/server/world/mcdb/leveldat" 8 "github.com/df-mc/goleveldb/leveldb" 9 "github.com/df-mc/goleveldb/leveldb/opt" 10 "github.com/sirupsen/logrus" 11 "os" 12 "path/filepath" 13 ) 14 15 // Logger is a logger implementation that may be passed to the Log field of Config. World will send errors and debug 16 // messages to this Logger when appropriate. 17 type Logger interface { 18 Errorf(format string, a ...any) 19 Debugf(format string, a ...any) 20 } 21 22 // Config holds the optional parameters of a DB. 23 type Config struct { 24 // Log is the Logger that will be used to log errors and debug messages to. 25 // If set to nil, a Logrus logger will be used. 26 Log Logger 27 // Compression specifies the compression to use for compressing new data in 28 // the database. Decompression of the database will happen based on IDs 29 // found in the compressed blocks and is therefore uninfluenced by this 30 // field. If left empty, Compression will default to opt.FlateCompression. 31 Compression opt.Compression 32 // BlockSize specifies the size of blocks to be compressed. The default 33 // value, when left empty, is 16KiB (16 * opt.KiB). Higher values generally 34 // lead to better compression ratios at the expense of slightly higher 35 // memory usage while (de)compressing. 36 BlockSize int 37 // ReadOnly opens the DB in read-only mode. This will leave the data in the 38 // database unedited. 39 ReadOnly bool 40 41 // Entities is an EntityRegistry with all entity types registered that may 42 // be read from the DB. Entities will default to entity.DefaultRegistry. 43 Entities world.EntityRegistry 44 } 45 46 // Open creates a new DB reading and writing from/to files under the path 47 // passed. If a world is present at the path, Open will parse its data and 48 // initialise the world with it. If the data cannot be parsed, an error is 49 // returned. 50 func (conf Config) Open(dir string) (*DB, error) { 51 if conf.Log == nil { 52 conf.Log = logrus.New() 53 } 54 if conf.BlockSize == 0 { 55 conf.BlockSize = 16 * opt.KiB 56 } 57 if len(conf.Entities.Types()) == 0 { 58 conf.Entities = entity.DefaultRegistry 59 } 60 _ = os.MkdirAll(filepath.Join(dir, "db"), 0777) 61 62 db := &DB{conf: conf, dir: dir, ldat: &leveldat.Data{}} 63 if _, err := os.Stat(filepath.Join(dir, "level.dat")); os.IsNotExist(err) { 64 // A level.dat was not currently present for the world. 65 db.ldat.FillDefault() 66 } else { 67 ldat, err := leveldat.ReadFile(filepath.Join(dir, "level.dat")) 68 if err != nil { 69 return nil, fmt.Errorf("open db: %w", err) 70 } 71 72 // TODO: Perform proper conversion here. Dragonfly stored 3 for a long 73 // time even though the fields were up to date, so we have to accept 74 // older ones no matter what. 75 ver := ldat.Ver() 76 if ver != leveldat.Version && ver >= 10 { 77 return nil, fmt.Errorf("open db: level.dat version %v is unsupported", ver) 78 } 79 if err = ldat.Unmarshal(db.ldat); err != nil { 80 return nil, fmt.Errorf("open db: %w", err) 81 } 82 } 83 db.set = db.ldat.Settings() 84 ldb, err := leveldb.OpenFile(filepath.Join(dir, "db"), &opt.Options{ 85 Compression: conf.Compression, 86 BlockSize: conf.BlockSize, 87 ReadOnly: conf.ReadOnly, 88 }) 89 if err != nil { 90 return nil, fmt.Errorf("error opening leveldb database: %w", err) 91 } 92 db.ldb = ldb 93 return db, nil 94 }