Merge pull request #200 from xtrafrancyz/master
Fix and improvements to mca
This commit is contained in:
@ -78,14 +78,19 @@ func Load(f io.ReadWriteSeeker) (r *Region, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create open .mca file with os.O_CREATE|os. O_EXCL, and init the region
|
// Create open .mca file with os.O_CREATE|os. O_EXCL, and init the region
|
||||||
func Create(name string) (r *Region, err error) {
|
func Create(name string) (*Region, error) {
|
||||||
r = new(Region)
|
f, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_EXCL, 0666)
|
||||||
r.sectors = make(map[int32]bool)
|
|
||||||
|
|
||||||
r.f, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_EXCL, 0666)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return CreateWriter(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateWriter init the region
|
||||||
|
func CreateWriter(f io.ReadWriteSeeker) (r *Region, err error) {
|
||||||
|
r = new(Region)
|
||||||
|
r.sectors = make(map[int32]bool)
|
||||||
|
r.f = f
|
||||||
|
|
||||||
// write the offsets
|
// write the offsets
|
||||||
err = binary.Write(r.f, binary.BigEndian, &r.offsets)
|
err = binary.Write(r.f, binary.BigEndian, &r.offsets)
|
||||||
@ -152,7 +157,7 @@ func (r *Region) ReadSector(x, z int) (data []byte, err error) {
|
|||||||
|
|
||||||
// WriteSector write Chunk data into region file
|
// WriteSector write Chunk data into region file
|
||||||
func (r *Region) WriteSector(x, z int, data []byte) error {
|
func (r *Region) WriteSector(x, z int, data []byte) error {
|
||||||
need := int32(len(data)+4)/4096 + 1
|
need := int32((len(data) + 4 + 4096 - 1) / 4096)
|
||||||
n, now := sectorLoc(r.offsets[z][x])
|
n, now := sectorLoc(r.offsets[z][x])
|
||||||
|
|
||||||
// maximum chunk size is 1MB
|
// maximum chunk size is 1MB
|
||||||
@ -212,6 +217,23 @@ func (r *Region) ExistSector(x, z int) bool {
|
|||||||
return r.offsets[z][x] != 0
|
return r.offsets[z][x] != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PadToFullSector writes zeros to the end of the file to make size a multiple of 4096
|
||||||
|
// Legacy versions of Minecraft require this
|
||||||
|
// Need to be called right before Close
|
||||||
|
func (r *Region) PadToFullSector() error {
|
||||||
|
size, err := r.f.Seek(0, io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if size%4096 != 0 {
|
||||||
|
_, err = r.f.Write(make([]byte, 4096-size%4096))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Region) findSpace(need int32) (n int32) {
|
func (r *Region) findSpace(need int32) (n int32) {
|
||||||
for i := int32(0); i < need; i++ {
|
for i := int32(0); i < need; i++ {
|
||||||
if r.sectors[n+i] {
|
if r.sectors[n+i] {
|
||||||
|
@ -3,8 +3,12 @@ package region
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/zlib"
|
"compress/zlib"
|
||||||
"github.com/Tnze/go-mc/nbt"
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/nbt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIn(t *testing.T) {
|
func TestIn(t *testing.T) {
|
||||||
@ -95,3 +99,73 @@ func TestCountChunks(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("chunk count: %d", count)
|
t.Logf("chunk count: %d", count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWriteSectors(t *testing.T) {
|
||||||
|
temp, err := os.CreateTemp("", "region*.mca")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := temp.Close(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if err := os.Remove(temp.Name()); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
region, err := CreateWriter(temp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedSectorsNum := 2
|
||||||
|
for idx, test := range []struct{ size, sectors int }{
|
||||||
|
{0, 1},
|
||||||
|
{1000, 1},
|
||||||
|
{4091, 1},
|
||||||
|
{4092, 1},
|
||||||
|
{4093, 2},
|
||||||
|
{5000, 2},
|
||||||
|
} {
|
||||||
|
expectedSectorsNum += test.sectors
|
||||||
|
|
||||||
|
data := make([]byte, test.size)
|
||||||
|
rand.Read(data)
|
||||||
|
if err = region.WriteSector(idx, 0, data); err != nil {
|
||||||
|
t.Fatal("write sector", err)
|
||||||
|
}
|
||||||
|
if len(region.sectors) != expectedSectorsNum {
|
||||||
|
t.Errorf("wrong region sector count. Got: %d, Want: %d", len(region.sectors), expectedSectorsNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if read, err := region.ReadSector(idx, 0); err != nil {
|
||||||
|
t.Fatal("read sector", err)
|
||||||
|
} else if !bytes.Equal(data, read) {
|
||||||
|
t.Fatal("read corrupted sector data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset file
|
||||||
|
if _, err = temp.Seek(0, io.SeekStart); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test load
|
||||||
|
region, err = Load(temp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("load region: %v", err)
|
||||||
|
}
|
||||||
|
if len(region.sectors) != expectedSectorsNum {
|
||||||
|
t.Fatalf("read sector count missmatch. Got: %d, Want: %d", len(region.sectors), expectedSectorsNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test padding
|
||||||
|
if err = region.PadToFullSector(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if stat, err := temp.Stat(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if stat.Size()%4096 != 0 {
|
||||||
|
t.Fatalf("wrong file size. Got %d, Want: %d", stat.Size(), stat.Size()+(4096-stat.Size()%4096))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user