package mcdb import ( "fmt" "log/slog" "os" "path/filepath" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/mcdb/leveldat" "github.com/df-mc/goleveldb/leveldb" "github.com/df-mc/goleveldb/leveldb/opt" ) // Config holds the optional parameters of a DB. type Config struct { // Log is the Logger that will be used to log errors and debug messages to. // If set to nil, Log is set to slog.Default(). Log *slog.Logger // LDBOptions holds LevelDB specific default options, such as the block size // or compression used in the database. LDBOptions *opt.Options // Blocks is the BlockRegistry used for chunk decoding/encoding. If nil, world.DefaultBlockRegistry is used. // When using a non-default registry, pass the same registry used by the World. Blocks world.BlockRegistry } // Open creates a new DB reading and writing from/to files under the path // passed. If a world is present at the path, Open will parse its data and // initialise the world with it. If the data cannot be parsed, an error is // returned. func (conf Config) Open(dir string) (*DB, error) { if conf.Log == nil { conf.Log = slog.Default() } conf.Log = conf.Log.With("provider", "mcdb") if conf.LDBOptions == nil { conf.LDBOptions = new(opt.Options) } if conf.LDBOptions.BlockSize == 0 { conf.LDBOptions.BlockSize = 16 * opt.KiB } _ = os.MkdirAll(filepath.Join(dir, "db"), 0777) db := &DB{conf: conf, dir: dir, ldat: &leveldat.Data{}} db.SetBlockRegistry(conf.Blocks) if _, err := os.Stat(filepath.Join(dir, "level.dat")); os.IsNotExist(err) { // A level.dat was not currently present for the world. db.ldat.FillDefault() } else { ldat, err := leveldat.ReadFile(filepath.Join(dir, "level.dat")) if err != nil { return nil, fmt.Errorf("open db: read level.dat: %w", err) } ver := ldat.Ver() if ver != leveldat.Version && ver >= 10 { return nil, fmt.Errorf("open db: level.dat version %v is unsupported", ver) } if err = ldat.Unmarshal(db.ldat); err != nil { return nil, fmt.Errorf("open db: unmarshal level.dat: %w", err) } } db.set = db.ldat.Settings() ldb, err := leveldb.OpenFile(filepath.Join(dir, "db"), conf.LDBOptions) if err != nil { return nil, fmt.Errorf("open db: leveldb: %w", err) } db.ldb = ldb return db, nil }