Phase4
This commit is contained in:
114
apps/node-agent/internal/world/world.go
Normal file
114
apps/node-agent/internal/world/world.go
Normal file
@@ -0,0 +1,114 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user