spatial-db/server/inmemory_server.go

119 lines
2.6 KiB
Go
Raw Normal View History

package server
import (
"errors"
"io/fs"
"os"
"path/filepath"
"strings"
"git.nicholasnovak.io/nnovak/spatial-db/storage"
"git.nicholasnovak.io/nnovak/spatial-db/world"
)
type InMemoryServer struct {
StorageDir string
Chunks map[world.ChunkPos]world.ChunkData
}
func (s *InMemoryServer) SetStorageRoot(path string) {
s.StorageDir = path
chunkFiles, err := os.ReadDir(s.StorageDir)
if err != nil {
panic(err)
}
s.Chunks = make(map[world.ChunkPos]world.ChunkData)
validChunkFiles := []fs.DirEntry{}
for _, chunkFile := range chunkFiles {
if chunkFile.IsDir() || !strings.HasSuffix(chunkFile.Name(), ".chunk") {
continue
}
validChunkFiles = append(validChunkFiles, chunkFile)
}
chunks := make([]world.ChunkData, len(validChunkFiles))
for chunkIndex, chunkFile := range validChunkFiles {
go func(index int, cf fs.DirEntry) {
file, err := os.Open(filepath.Join(s.StorageDir, cf.Name()))
if err != nil {
panic(err)
}
chunkData, err := storage.ReadChunkFromFile(file)
if err != nil {
panic(err)
}
file.Close()
chunks[index] = chunkData
}(chunkIndex, chunkFile)
}
for _, chunkData := range chunks {
s.Chunks[chunkData.Pos] = chunkData
}
}
func (s *InMemoryServer) FetchOrCreateChunk(pos world.ChunkPos) (world.ChunkData, error) {
return s.Chunks[pos], nil
}
func (s *InMemoryServer) FetchChunk(pos world.ChunkPos) (world.ChunkData, error) {
chunkFileName := filepath.Join(s.StorageDir, pos.ToFileName())
var chunkData world.ChunkData
chunkFile, err := os.Open(chunkFileName)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return chunkData, storage.ChunkNotFoundError
} else {
return chunkData, err
}
}
defer chunkFile.Close()
return storage.ReadChunkFromFile(chunkFile)
}
// Voxel server implementation
func (s *InMemoryServer) ChangeBlock(
worldPosition world.BlockPos,
targetState world.BlockID,
) error {
chunk, err := s.FetchOrCreateChunk(worldPosition.ToChunkPos())
if err != nil {
return err
}
chunk.SectionFor(worldPosition).UpdateBlock(worldPosition, targetState)
return nil
}
func (s *InMemoryServer) ChangeBlockRange(
targetState world.BlockID,
start, end world.BlockPos,
) error {
panic("ChangeBlockRange is unimplemented")
}
func (s *InMemoryServer) ReadBlockAt(pos world.BlockPos) (world.BlockID, error) {
chunk, err := s.FetchOrCreateChunk(pos.ToChunkPos())
if err != nil {
return world.Empty, err
}
return chunk.SectionFor(pos).FetchBlock(pos), nil
}
func (s *InMemoryServer) ReadChunkAt(pos world.ChunkPos) (world.ChunkData, error) {
return s.FetchOrCreateChunk(pos)
}