From 19fd388cd90f3f1f2ec84ee229dd54640d27d378 Mon Sep 17 00:00:00 2001 From: Tnze Date: Sat, 3 Aug 2019 14:34:51 +0800 Subject: [PATCH] write mca region file (test and optimization) --- save/region/mca.go | 87 +++++++++++++++++++++++------------------ save/region/mca_test.go | 67 +++++++++++++++---------------- 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/save/region/mca.go b/save/region/mca.go index f009013..9ae3593 100644 --- a/save/region/mca.go +++ b/save/region/mca.go @@ -5,6 +5,7 @@ import ( "errors" "io" "os" + "time" ) type Region struct { @@ -67,26 +68,6 @@ func OpenRegion(name string) (r *Region, err error) { } func (r *Region) Close() error { - _, err := r.f.Seek(0, 0) - if err != nil { - return err - } - - // read the offsets - err = binary.Write(r.f, binary.BigEndian, &r.offsets) - if err != nil { - _ = r.f.Close() - return err - } - r.sectors[0] = true - - // read the timestamps - err = binary.Write(r.f, binary.BigEndian, &r.timestamps) - if err != nil { - _ = r.f.Close() - return err - } - return r.f.Close() } @@ -119,42 +100,43 @@ func (r *Region) ReadSector(x, y int) (data []byte, err error) { } func (r *Region) WriteSector(x, y int, data []byte) error { - sectorsNeeded := int32(len(data)+4)/4096 + 1 - sectorNumber, sectorsAllocated := sectorLoc(r.offsets[x][y]) + need := int32(len(data)+4)/4096 + 1 + n, now := sectorLoc(r.offsets[x][y]) // maximum chunk size is 1MB - if sectorsNeeded >= 256 { + if need >= 256 { return errors.New("data too large") } - if sectorNumber != 0 && sectorsAllocated == sectorsNeeded { + if n != 0 && now == need { // we can simply overwrite the old sectors } else { // we need to allocate new sectors // mark the sectors previously used for this chunk as free - for i := int32(0); i < sectorsAllocated; i++ { - r.sectors[sectorNumber+i] = false + for i := int32(0); i < now; i++ { + r.sectors[n+i] = false } // scan for a free space large enough to store this chunk - sectorNumber = 0 - for i := int32(0); i < sectorsNeeded; i++ { - if r.sectors[sectorNumber+i] { - sectorNumber += i + 1 - i = -1 - } - } + n = r.findSpace(need) // mark the sectors previously used for this chunk as used - sectorsAllocated = sectorsNeeded - for i := int32(0); i < sectorsNeeded; i++ { - r.sectors[sectorNumber+i] = true + now = need + for i := int32(0); i < need; i++ { + r.sectors[n+i] = true + } + + r.offsets[x][y] = (n << 8) | (need & 0xFF) + + // update file head + err := r.setHead(x, y, uint32(r.offsets[x][y]), uint32(time.Now().Unix())) + if err != nil { + return err } - r.offsets[x][y] = (sectorNumber << 8) | (sectorsNeeded & 0xFF) } - _, err := r.f.Seek(4096*int64(sectorNumber), 0) + _, err := r.f.Seek(4096*int64(n), 0) if err != nil { return err } @@ -163,6 +145,7 @@ func (r *Region) WriteSector(x, y int, data []byte) error { if err != nil { return err } + //data _, err = r.f.Write(data) if err != nil { @@ -171,3 +154,31 @@ func (r *Region) WriteSector(x, y int, data []byte) error { return nil } + +func (r *Region) findSpace(need int32) (n int32) { + for i := int32(0); i < need; i++ { + if r.sectors[n+i] { + n += i + 1 + i = -1 + } + } + return +} + +func (r *Region) setHead(x, y int, offset, timestamp uint32) (err error) { + var buf [4]byte + + binary.BigEndian.PutUint32(buf[:], offset) + _, err = r.f.WriteAt(buf[:], 4*(int64(x)*32+int64(y))) + if err != nil { + return + } + + binary.BigEndian.PutUint32(buf[:], timestamp) + _, err = r.f.WriteAt(buf[:], 4096+4*(int64(x)*32+int64(y))) + if err != nil { + return + } + + return +} diff --git a/save/region/mca_test.go b/save/region/mca_test.go index cc3859c..ca061a9 100644 --- a/save/region/mca_test.go +++ b/save/region/mca_test.go @@ -3,7 +3,7 @@ package region import ( "bytes" "compress/zlib" - "io/ioutil" + "github.com/Tnze/go-mc/nbt" "testing" ) @@ -26,46 +26,43 @@ func TestReadRegion(t *testing.T) { if err != nil { t.Fatal(err) } + defer r.Close() - s, err := r.ReadSector(In(31, 0)) - if err != nil { - t.Fatal(err) + for i := 0; i < 32; i++ { + for j := 0; j < 32; j++ { + s, err := r.ReadSector(In(i, j)) + if err != nil { + continue + } + + r, err := zlib.NewReader(bytes.NewReader(s[1:])) + if err != nil { + t.Error(err) + } + var b interface{} + err = nbt.NewDecoder(r).Decode(&b) + if err != nil { + t.Error(err) + } + //t.Log(b) + } } - reader, err := zlib.NewReader(bytes.NewReader(s[1:])) - if err != nil { - t.Fatal(err) - } - - s, err = ioutil.ReadAll(reader) - if err != nil { - t.Fatal(err) - } - - t.Log(s) } -func TestSectorsFinder(t *testing.T) { - sectors := []byte{ - 1, 1, - 0, 1, //2 - 0, 0, 1, //4 - 0, 0, 0, 1, 1, //7 - 0, 0, 0, 0, 0, 1, //12 - 0, 0, 0, 0, 0, 0, 0, //18 +func TestFindSpace(t *testing.T) { + r := &Region{ + sectors: map[int32]bool{ + 0: true, 1: true, + 2: false, 3: true, + 4: false, 5: false, 6: true, + 7: false, 8: false, 9: false, 10: true, 11: true, + 12: false, 13: false, 14: false, 15: false, 16: false, 17: true, + 18: false, 19: false, 20: false, 21: false, 22: false, 23: false, 24: false, + }, } - scan := func(need int) (index int) { - for i := 0; i < need; i++ { - if sectors[index+i] != 0 { - index += i + 1 - i = -1 // 0 for next loop - } - } - return - } - - for _, test := range []struct{ need, index int }{ + for _, test := range []struct{ need, index int32 }{ {0, 0}, {1, 2}, {2, 4}, @@ -74,7 +71,7 @@ func TestSectorsFinder(t *testing.T) { {5, 12}, {6, 18}, } { - i := scan(test.need) + i := r.findSpace(test.need) if i != test.index { t.Errorf("scan sctors fail: get %d, want %d", i, test.index) }