115 lines
2.5 KiB
Go
115 lines
2.5 KiB
Go
package world
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Summary describes a discovered Minecraft world directory.
|
|
type Summary struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
SizeBytes int64 `json:"sizeBytes"`
|
|
HasLevelDat bool `json:"hasLevelDat"`
|
|
}
|
|
|
|
// ValidateWorld checks that a world directory contains level.dat.
|
|
func ValidateWorld(dataPath, worldName string) error {
|
|
worldName = strings.TrimSpace(worldName)
|
|
if worldName == "" || strings.Contains(worldName, "..") || strings.ContainsAny(worldName, `/\`) {
|
|
return fmt.Errorf("invalid world name")
|
|
}
|
|
|
|
worldDir := filepath.Join(dataPath, worldName)
|
|
levelDat := filepath.Join(worldDir, "level.dat")
|
|
|
|
info, err := os.Stat(levelDat)
|
|
if err != nil {
|
|
return fmt.Errorf("level.dat not found in world directory")
|
|
}
|
|
if info.IsDir() {
|
|
return fmt.Errorf("level.dat is not a file")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListWorlds scans the server data directory for world folders.
|
|
func ListWorlds(dataPath, activeWorldName string) ([]Summary, error) {
|
|
entries, err := os.ReadDir(dataPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
worlds := make([]Summary, 0)
|
|
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
|
|
continue
|
|
}
|
|
|
|
worldPath := filepath.Join(dataPath, entry.Name())
|
|
levelDat := filepath.Join(worldPath, "level.dat")
|
|
hasLevelDat := false
|
|
if info, err := os.Stat(levelDat); err == nil && !info.IsDir() {
|
|
hasLevelDat = true
|
|
}
|
|
|
|
size, err := dirSize(worldPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
worlds = append(worlds, Summary{
|
|
Name: entry.Name(),
|
|
Path: entry.Name(),
|
|
SizeBytes: size,
|
|
HasLevelDat: hasLevelDat,
|
|
})
|
|
}
|
|
|
|
if activeWorldName != "" {
|
|
found := false
|
|
for _, world := range worlds {
|
|
if world.Name == activeWorldName {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
if err := ValidateWorld(dataPath, activeWorldName); err == nil {
|
|
size, sizeErr := dirSize(filepath.Join(dataPath, activeWorldName))
|
|
if sizeErr != nil {
|
|
return nil, sizeErr
|
|
}
|
|
worlds = append(worlds, Summary{
|
|
Name: activeWorldName,
|
|
Path: activeWorldName,
|
|
SizeBytes: size,
|
|
HasLevelDat: true,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return worlds, nil
|
|
}
|
|
|
|
func dirSize(root string) (int64, error) {
|
|
var total int64
|
|
|
|
err := filepath.Walk(root, func(_ string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
total += info.Size()
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return total, err
|
|
}
|