From 73c93af328bb4dc3e6b40af42641eba758df277f Mon Sep 17 00:00:00 2001 From: BuckarooBanzay Date: Tue, 12 Oct 2021 07:44:07 +0200 Subject: [PATCH] partial switch to mapparser lib --- go.mod | 1 + go.sum | 4 + mapblockaccessor/get.go | 10 +- mapblockaccessor/legacyblocks.go | 8 +- mapblockaccessor/mtime.go | 8 +- mapblockaccessor/update.go | 4 +- mapblockparser/countedreader.go | 22 --- mapblockparser/iterate.go | 11 -- mapblockparser/logger.go | 11 -- mapblockparser/mapblock.go | 148 --------------- mapblockparser/mapdata.go | 47 ----- mapblockparser/metadata.go | 180 ------------------- mapblockparser/parse.go | 112 ------------ mapblockparser/parse_test.go | 122 ------------- mapblockparser/prometheus.go | 24 --- mapblockparser/testdata/0.-1.0 | Bin 1295 -> 0 bytes mapblockparser/testdata/0.0.0 | Bin 3213 -> 0 bytes mapblockparser/testdata/0.1.0 | Bin 829 -> 0 bytes mapblockparser/testdata/0.10.0 | Bin 146 -> 0 bytes mapblockparser/testdata/0.2.0 | Bin 653 -> 0 bytes mapblockparser/testdata/0.3.0 | Bin 168 -> 0 bytes mapblockparser/testdata/0.4.0 | Bin 165 -> 0 bytes mapblockparser/testdata/0.5.0 | Bin 170 -> 0 bytes mapblockparser/testdata/0.6.0 | Bin 170 -> 0 bytes mapblockparser/testdata/0.7.0 | Bin 169 -> 0 bytes mapblockparser/testdata/0.8.0 | Bin 169 -> 0 bytes mapblockparser/testdata/0.9.0 | Bin 951 -> 0 bytes mapblockparser/testdata/11.0.2 | Bin 6285 -> 0 bytes mapblockparser/testdata/mb-with-metadata.bin | Bin 1040 -> 0 bytes mapobject/atm.go | 8 +- mapobject/bones.go | 8 +- mapobject/border.go | 8 +- mapobject/digilinelcd.go | 8 +- mapobject/digiterms.go | 8 +- mapobject/fancyvend.go | 9 +- mapobject/jumpdrive.go | 8 +- mapobject/label.go | 8 +- mapobject/locator.go | 8 +- mapobject/luacontroller.go | 8 +- mapobject/mission.go | 8 +- mapobject/nuclearreactor.go | 8 +- mapobject/poi.go | 8 +- mapobject/privprotector.go | 8 +- mapobject/protector.go | 8 +- mapobject/quarry.go | 8 +- mapobject/sign.go | 8 +- mapobject/smartshop.go | 8 +- mapobject/technicanchor.go | 8 +- mapobject/technicswitch.go | 8 +- mapobject/train.go | 8 +- mapobject/travelnet.go | 8 +- mapobject/xpprotector.go | 8 +- tilerendererjob/common.go | 4 +- 53 files changed, 137 insertions(+), 764 deletions(-) delete mode 100644 mapblockparser/countedreader.go delete mode 100644 mapblockparser/iterate.go delete mode 100644 mapblockparser/logger.go delete mode 100644 mapblockparser/mapblock.go delete mode 100644 mapblockparser/mapdata.go delete mode 100644 mapblockparser/metadata.go delete mode 100644 mapblockparser/parse.go delete mode 100644 mapblockparser/parse_test.go delete mode 100644 mapblockparser/prometheus.go delete mode 100644 mapblockparser/testdata/0.-1.0 delete mode 100644 mapblockparser/testdata/0.0.0 delete mode 100644 mapblockparser/testdata/0.1.0 delete mode 100644 mapblockparser/testdata/0.10.0 delete mode 100644 mapblockparser/testdata/0.2.0 delete mode 100644 mapblockparser/testdata/0.3.0 delete mode 100644 mapblockparser/testdata/0.4.0 delete mode 100644 mapblockparser/testdata/0.5.0 delete mode 100644 mapblockparser/testdata/0.6.0 delete mode 100644 mapblockparser/testdata/0.7.0 delete mode 100644 mapblockparser/testdata/0.8.0 delete mode 100644 mapblockparser/testdata/0.9.0 delete mode 100644 mapblockparser/testdata/11.0.2 delete mode 100644 mapblockparser/testdata/mb-with-metadata.bin diff --git a/go.mod b/go.mod index 5c591af..cfe3918 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/lib/pq v1.10.2 github.com/mattn/go-sqlite3 v1.14.8 + github.com/minetest-go/mapparser v0.1.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.11.0 github.com/sirupsen/logrus v1.8.1 diff --git a/go.sum b/go.sum index d11e398..07140ed 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -64,6 +66,8 @@ github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxz github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/minetest-go/mapparser v0.1.0 h1:wjVrS2QXURnphPlqqPRD6vt1gvkT55RRUjroT3G5Two= +github.com/minetest-go/mapparser v0.1.0/go.mod h1:Jnf9+Oe8Hs8IJw6Tk8XPd72NTJkjxLOamTpO51KJoXU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= diff --git a/mapblockaccessor/get.go b/mapblockaccessor/get.go index 6e16e2f..eafaf45 100644 --- a/mapblockaccessor/get.go +++ b/mapblockaccessor/get.go @@ -3,9 +3,9 @@ package mapblockaccessor import ( "mapserver/coords" "mapserver/eventbus" - "mapserver/mapblockparser" "sync" + "github.com/minetest-go/mapparser" cache "github.com/patrickmn/go-cache" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -13,7 +13,7 @@ import ( var lock = &sync.RWMutex{} -func (a *MapBlockAccessor) GetMapBlock(pos *coords.MapBlockCoords) (*mapblockparser.MapBlock, error) { +func (a *MapBlockAccessor) GetMapBlock(pos *coords.MapBlockCoords) (*mapparser.MapBlock, error) { key := getKey(pos) //maintenance @@ -40,7 +40,7 @@ func (a *MapBlockAccessor) GetMapBlock(pos *coords.MapBlockCoords) (*mapblockpar if cachedblock == nil { return nil, nil } else { - return cachedblock.(*mapblockparser.MapBlock), nil + return cachedblock.(*mapparser.MapBlock), nil } } @@ -61,7 +61,7 @@ func (a *MapBlockAccessor) GetMapBlock(pos *coords.MapBlockCoords) (*mapblockpar if cachedblock == nil { return nil, nil } else { - return cachedblock.(*mapblockparser.MapBlock), nil + return cachedblock.(*mapparser.MapBlock), nil } } @@ -79,7 +79,7 @@ func (a *MapBlockAccessor) GetMapBlock(pos *coords.MapBlockCoords) (*mapblockpar getCacheMissCount.Inc() - mapblock, err := mapblockparser.Parse(block.Data, block.Mtime, pos) + mapblock, err := mapparser.Parse(block.Data) if err != nil { return nil, err } diff --git a/mapblockaccessor/legacyblocks.go b/mapblockaccessor/legacyblocks.go index 597f29c..ca6f8f6 100644 --- a/mapblockaccessor/legacyblocks.go +++ b/mapblockaccessor/legacyblocks.go @@ -3,16 +3,16 @@ package mapblockaccessor import ( "mapserver/eventbus" "mapserver/layer" - "mapserver/mapblockparser" "mapserver/settings" + "github.com/minetest-go/mapparser" cache "github.com/patrickmn/go-cache" "github.com/sirupsen/logrus" ) type FindNextLegacyBlocksResult struct { HasMore bool - List []*mapblockparser.MapBlock + List []*mapparser.MapBlock UnfilteredCount int Progress float64 LastMtime int64 @@ -29,7 +29,7 @@ func (a *MapBlockAccessor) FindNextLegacyBlocks(s settings.Settings, layers []*l blocks := nextResult.List result := FindNextLegacyBlocksResult{} - mblist := make([]*mapblockparser.MapBlock, 0) + mblist := make([]*mapparser.MapBlock, 0) result.HasMore = nextResult.HasMore result.UnfilteredCount = nextResult.UnfilteredCount result.Progress = nextResult.Progress @@ -46,7 +46,7 @@ func (a *MapBlockAccessor) FindNextLegacyBlocks(s settings.Settings, layers []*l key := getKey(block.Pos) - mapblock, err := mapblockparser.Parse(block.Data, block.Mtime, block.Pos) + mapblock, err := mapparser.Parse(block.Data) if err != nil { fields := logrus.Fields{ "x": block.Pos.X, diff --git a/mapblockaccessor/mtime.go b/mapblockaccessor/mtime.go index d0a5cb0..3c66a1a 100644 --- a/mapblockaccessor/mtime.go +++ b/mapblockaccessor/mtime.go @@ -4,8 +4,8 @@ import ( "mapserver/coords" "mapserver/eventbus" "mapserver/layer" - "mapserver/mapblockparser" + "github.com/minetest-go/mapparser" cache "github.com/patrickmn/go-cache" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -15,7 +15,7 @@ type FindMapBlocksByMtimeResult struct { HasMore bool LastPos *coords.MapBlockCoords LastMtime int64 - List []*mapblockparser.MapBlock + List []*mapparser.MapBlock UnfilteredCount int } @@ -39,7 +39,7 @@ func (a *MapBlockAccessor) FindMapBlocksByMtime(lastmtime int64, limit int, laye result := FindMapBlocksByMtimeResult{} - mblist := make([]*mapblockparser.MapBlock, 0) + mblist := make([]*mapparser.MapBlock, 0) var newlastpos *coords.MapBlockCoords result.HasMore = len(blocks) == limit result.UnfilteredCount = len(blocks) @@ -65,7 +65,7 @@ func (a *MapBlockAccessor) FindMapBlocksByMtime(lastmtime int64, limit int, laye key := getKey(block.Pos) - mapblock, err := mapblockparser.Parse(block.Data, block.Mtime, block.Pos) + mapblock, err := mapparser.Parse(block.Data) if err != nil { fields := logrus.Fields{ "x": block.Pos.X, diff --git a/mapblockaccessor/update.go b/mapblockaccessor/update.go index 02c80d3..ba81796 100644 --- a/mapblockaccessor/update.go +++ b/mapblockaccessor/update.go @@ -2,12 +2,12 @@ package mapblockaccessor import ( "mapserver/coords" - "mapserver/mapblockparser" + "github.com/minetest-go/mapparser" cache "github.com/patrickmn/go-cache" ) -func (a *MapBlockAccessor) Update(pos *coords.MapBlockCoords, mb *mapblockparser.MapBlock) { +func (a *MapBlockAccessor) Update(pos *coords.MapBlockCoords, mb *mapparser.MapBlock) { key := getKey(pos) cacheBlockCount.Inc() a.blockcache.Set(key, mb, cache.DefaultExpiration) diff --git a/mapblockparser/countedreader.go b/mapblockparser/countedreader.go deleted file mode 100644 index a268258..0000000 --- a/mapblockparser/countedreader.go +++ /dev/null @@ -1,22 +0,0 @@ -package mapblockparser - -import ( - "bytes" -) - -type CountedReader struct { - Reader *bytes.Reader - Count int -} - -func (r *CountedReader) Read(p []byte) (int, error) { - i, err := r.Reader.Read(p) - r.Count += i - return i, err -} - -func (r *CountedReader) ReadByte() (byte, error) { - i, err := r.Reader.ReadByte() - r.Count++ - return i, err -} diff --git a/mapblockparser/iterate.go b/mapblockparser/iterate.go deleted file mode 100644 index 6beea0b..0000000 --- a/mapblockparser/iterate.go +++ /dev/null @@ -1,11 +0,0 @@ -package mapblockparser - -func IterateMapblock(cb func(x, y, z int)) { - for x := 0; x < 16; x++ { - for y := 0; y < 16; y++ { - for z := 0; z < 16; z++ { - cb(x, y, z) - } - } - } -} diff --git a/mapblockparser/logger.go b/mapblockparser/logger.go deleted file mode 100644 index 497ea67..0000000 --- a/mapblockparser/logger.go +++ /dev/null @@ -1,11 +0,0 @@ -package mapblockparser - -import ( - "github.com/sirupsen/logrus" -) - -var log *logrus.Entry - -func init() { - log = logrus.WithFields(logrus.Fields{"prefix": "mapblockaccessor"}) -} diff --git a/mapblockparser/mapblock.go b/mapblockparser/mapblock.go deleted file mode 100644 index 36438c0..0000000 --- a/mapblockparser/mapblock.go +++ /dev/null @@ -1,148 +0,0 @@ -package mapblockparser - -import ( - "mapserver/coords" -) - -type MapBlock struct { - Pos *coords.MapBlockCoords `json:"pos"` - Size int `json:"size"` - Version byte `json:"version"` - Underground bool `json:"underground"` - Mapdata *MapData `json:"mapdata"` - Metadata *Metadata `json:"metadata"` - BlockMapping map[int]string `json:"blockmapping"` - Mtime int64 `json:"mtime"` -} - -type MapData struct { - ContentId []int `json:"contentid"` - Param1 []int `json:"param1"` - Param2 []int `json:"param2"` -} - -type Metadata struct { - Inventories map[int]map[string]*Inventory `json:"inventories"` - Pairs map[int]map[string]string `json:"pairs"` -} - -type Item struct { - Name string `json:"name"` - Count int `json:"count"` - Wear int `json:"wear"` - //TODO: metadata -} - -func (this *MapBlock) IsEmpty() bool { - if len(this.BlockMapping) == 0 { - // only air - return true - } - - if len(this.BlockMapping) == 1 { - for _, name := range this.BlockMapping { - if name == "vacuum:vacuum" { - // only vacuum - return true - } - } - } - - // other stuff - return false -} - -func (this *Item) IsEmpty() bool { - return this.Name == "" && this.Count == 0 -} - -type Inventory struct { - Size int `json:"size"` - Items []*Item `json:"items"` -} - -func getNodePos(x, y, z int) int { - return x + (y * 16) + (z * 256) -} - -func (inv *Inventory) IsEmpty() bool { - if len(inv.Items) == 0 { - return true - } - - for _, item := range inv.Items { - if item.Name != "" && item.Count > 0 { - return false - } - } - - return true -} - -func (mb *MapBlock) GetNodeId(x, y, z int) int { - pos := getNodePos(x, y, z) - return mb.Mapdata.ContentId[pos] -} - -func (mb *MapBlock) GetParam2(x, y, z int) int { - pos := getNodePos(x, y, z) - return mb.Mapdata.Param2[pos] -} - -func (mb *MapBlock) GetNodeName(x, y, z int) string { - id := mb.GetNodeId(x, y, z) - return mb.BlockMapping[id] -} - -func NewMapblock() *MapBlock { - mb := MapBlock{} - mb.Metadata = NewMetadata() - mb.BlockMapping = make(map[int]string) - return &mb -} - -func NewMetadata() *Metadata { - md := Metadata{} - md.Inventories = make(map[int]map[string]*Inventory) - md.Pairs = make(map[int]map[string]string) - return &md -} - -func (md *Metadata) GetMetadata(x, y, z int) map[string]string { - return md.GetPairsMap(getNodePos(x, y, z)) -} - -func (md *Metadata) GetPairsMap(pos int) map[string]string { - pairsMap := md.Pairs[pos] - if pairsMap == nil { - pairsMap = make(map[string]string) - md.Pairs[pos] = pairsMap - } - - return pairsMap -} - -func (md *Metadata) GetInventoryMap(pos int) map[string]*Inventory { - invMap := md.Inventories[pos] - if invMap == nil { - invMap = make(map[string]*Inventory) - md.Inventories[pos] = invMap - } - - return invMap -} - -func (md *Metadata) GetInventoryMapAtPos(x, y, z int) map[string]*Inventory { - return md.GetInventoryMap(getNodePos(x, y, z)) -} - -func (md *Metadata) GetInventory(pos int, name string) *Inventory { - m := md.GetInventoryMap(pos) - inv := m[name] - if inv == nil { - inv = &Inventory{} - m[name] = inv - } - - return inv -} diff --git a/mapblockparser/mapdata.go b/mapblockparser/mapdata.go deleted file mode 100644 index 6d7b1ba..0000000 --- a/mapblockparser/mapdata.go +++ /dev/null @@ -1,47 +0,0 @@ -package mapblockparser - -import ( - "bytes" - "compress/zlib" - "errors" - "io" - "strconv" -) - -func parseMapdata(mapblock *MapBlock, data []byte) (int, error) { - r := bytes.NewReader(data) - - cr := new(CountedReader) - cr.Reader = r - - z, err := zlib.NewReader(cr) - if err != nil { - return 0, err - } - - defer z.Close() - - buf := new(bytes.Buffer) - io.Copy(buf, z) - - if buf.Len() != 16384 { - return 0, errors.New("Mapdata length invalid: " + strconv.Itoa(buf.Len())) - } - - rawdata := buf.Bytes() - - mapd := MapData{ - ContentId: make([]int, 4096), - Param1: make([]int, 4096), - Param2: make([]int, 4096), - } - mapblock.Mapdata = &mapd - - for i := 0; i < 4096; i++ { - mapd.ContentId[i] = readU16(rawdata, i*2) - mapd.Param1[i] = readU8(rawdata, (4096*2)+i) - mapd.Param2[i] = readU8(rawdata, (4096*3)+i) - } - - return cr.Count, nil -} diff --git a/mapblockparser/metadata.go b/mapblockparser/metadata.go deleted file mode 100644 index 98cf529..0000000 --- a/mapblockparser/metadata.go +++ /dev/null @@ -1,180 +0,0 @@ -package mapblockparser - -import ( - "bufio" - "bytes" - "compress/zlib" - "errors" - "io" - "strconv" - "strings" - - "github.com/sirupsen/logrus" -) - -/* -lua vm: https://github.com/yuin/gopher-lua -*/ - -const ( - INVENTORY_TERMINATOR = "EndInventory" - INVENTORY_END = "EndInventoryList" - INVENTORY_START = "List" -) - -func readU16(data []byte, offset int) int { - return (int(data[offset]) << 8) | int(data[offset+1]) -} - -func readU8(data []byte, offset int) int { - return int(data[offset]) -} - -func readU32(data []byte, offset int) int { - return int(data[offset])<<24 | - int(data[offset+1])<<16 | - int(data[offset+2])<<8 | - int(data[offset+3]) -} - -func parseMetadata(mapblock *MapBlock, data []byte) (int, error) { - r := bytes.NewReader(data) - - cr := new(CountedReader) - cr.Reader = r - - z, err := zlib.NewReader(cr) - if err != nil { - return 0, err - } - - defer z.Close() - - buf := new(bytes.Buffer) - io.Copy(buf, z) - - if cr.Count == 0 { - return 0, errors.New("no data") - } - - metadata := buf.Bytes() - - log.WithFields(logrus.Fields{"metadata-length": len(metadata)}).Trace("Parsing metadata") - - offset := 0 - version := metadata[offset] - - if version == 0 { - //No data? - return cr.Count, nil - } - - offset++ - count := readU16(metadata, offset) - - offset += 2 - - for i := 0; i < count; i++ { - position := readU16(metadata, offset) - pairsMap := mapblock.Metadata.GetPairsMap(position) - - offset += 2 - valuecount := readU32(metadata, offset) - - offset += 4 - for j := 0; j < valuecount; j++ { - keyLength := readU16(metadata, offset) - offset += 2 - - key := string(metadata[offset : keyLength+offset]) - offset += keyLength - - valueLength := readU32(metadata, offset) - offset += 4 - - if len(metadata) <= valueLength+offset { - return 0, errors.New("metadata too short: " + strconv.Itoa(len(metadata)) + - ", valuelength: " + strconv.Itoa(int(valueLength))) - } - - value := string(metadata[offset : valueLength+offset]) - offset += valueLength - - pairsMap[key] = value - - priv := 0 - if version >= 2 { /* private tag doesn't exist in version=1 */ - priv = readU8(metadata, offset) - offset++ - } - - if priv != 0 { - // do something usefull - logrus.Trace("Private items in Inventory") - } - } - - var currentInventoryName *string - var currentInventory *Inventory - - scanner := bufio.NewScanner(bytes.NewReader(metadata[offset:])) - for scanner.Scan() { - txt := scanner.Text() - offset += len(txt) + 1 - - log.WithFields(logrus.Fields{"txt": txt, "position": position}).Trace("Parsing inventory") - - if strings.HasPrefix(txt, INVENTORY_START) { - pairs := strings.Split(txt, " ") - currentInventoryName = &pairs[1] - currentInventory = mapblock.Metadata.GetInventory(position, *currentInventoryName) - currentInventory.Size = 0 - - } else if txt == INVENTORY_END { - currentInventoryName = nil - currentInventory = nil - } else if currentInventory != nil { - //content - if strings.HasPrefix(txt, "Item") { - item := Item{} - parts := strings.Split(txt, " ") - - if len(parts) >= 2 { - item.Name = parts[1] - } - - if len(parts) >= 3 { - val, err := strconv.ParseInt(parts[2], 10, 32) - if err != nil { - return 0, err - } - item.Count = int(val) - } - - if len(parts) >= 4 { - val, err := strconv.ParseInt(parts[3], 10, 32) - if err != nil { - return 0, err - } - item.Count = int(val) - } - - currentInventory.Items = append(currentInventory.Items, &item) - currentInventory.Size += 1 - - } - - } else if txt == INVENTORY_TERMINATOR { - break - - } else { - return 0, errors.New("Malformed inventory: " + txt) - } - } - - //TODO - - } - - return cr.Count, nil -} diff --git a/mapblockparser/parse.go b/mapblockparser/parse.go deleted file mode 100644 index 36ba7d5..0000000 --- a/mapblockparser/parse.go +++ /dev/null @@ -1,112 +0,0 @@ -package mapblockparser - -import ( - "errors" - "github.com/prometheus/client_golang/prometheus" - "mapserver/coords" - "strconv" -) - -func Parse(data []byte, mtime int64, pos *coords.MapBlockCoords) (*MapBlock, error) { - if len(data) == 0 { - return nil, errors.New("no data") - } - - timer := prometheus.NewTimer(parseDuration) - defer timer.ObserveDuration() - - mapblock := NewMapblock() - mapblock.Mtime = mtime - mapblock.Pos = pos - mapblock.Size = len(data) - - // version - mapblock.Version = data[0] - - if mapblock.Version < 25 || mapblock.Version > 28 { - return nil, errors.New("mapblock-version not supported: " + strconv.Itoa(int(mapblock.Version))) - } - - //flags - flags := data[1] - mapblock.Underground = (flags & 0x01) == 0x01 - - var offset int - - if mapblock.Version >= 27 { - offset = 4 - } else { - //u16 lighting_complete not present - offset = 2 - } - - content_width := data[offset] - params_width := data[offset+1] - - if content_width != 2 { - return nil, errors.New("content_width = " + strconv.Itoa(int(content_width))) - } - - if params_width != 2 { - return nil, errors.New("params_width = " + strconv.Itoa(int(params_width))) - } - - //mapdata (blocks) - if mapblock.Version >= 27 { - offset = 6 - - } else { - offset = 4 - - } - - //metadata - count, err := parseMapdata(mapblock, data[offset:]) - if err != nil { - return nil, err - } - - offset += count - - count, err = parseMetadata(mapblock, data[offset:]) - if err != nil { - return nil, err - } - - offset += count - - //static objects - - offset++ //static objects version - staticObjectsCount := readU16(data, offset) - offset += 2 - for i := 0; i < staticObjectsCount; i++ { - offset += 13 - dataSize := readU16(data, offset) - offset += dataSize + 2 - } - - //timestamp - offset += 4 - - //mapping version - offset++ - - numMappings := readU16(data, offset) - offset += 2 - for i := 0; i < numMappings; i++ { - nodeId := readU16(data, offset) - offset += 2 - - nameLen := readU16(data, offset) - offset += 2 - - blockName := string(data[offset : offset+nameLen]) - offset += nameLen - - mapblock.BlockMapping[nodeId] = blockName - } - - parsedMapBlocks.Inc() - return mapblock, nil -} diff --git a/mapblockparser/parse_test.go b/mapblockparser/parse_test.go deleted file mode 100644 index 67dc082..0000000 --- a/mapblockparser/parse_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package mapblockparser - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "mapserver/coords" - "strconv" - "testing" -) - -func TestReadU16(t *testing.T) { - v := readU16([]byte{0x00, 0x00}, 0) - if v != 0 { - t.Error(v) - } - - v = readU16([]byte{0x00, 0x01}, 0) - if v != 1 { - t.Error(v) - } - - v = readU16([]byte{0x01, 0x00}, 0) - if v != 256 { - t.Error(v) - } - -} -func TestReadU32(t *testing.T) { - v := readU32([]byte{0x00, 0x00, 0x00, 0x00}, 0) - if v != 0 { - t.Error(v) - } -} - -func TestParse(t *testing.T) { - - data, err := ioutil.ReadFile("testdata/0.0.0") - if err != nil { - t.Error(err) - } - - mapblock, err := Parse(data, 0, coords.NewMapBlockCoords(0, 0, 0)) - - if err != nil { - t.Error(err) - } - - if mapblock.Version != 28 { - t.Error("wrong mapblock version: " + strconv.Itoa(int(mapblock.Version))) - } - - if !mapblock.Underground { - t.Error("Underground flag") - } - - if len(mapblock.Mapdata.ContentId) != 4096 { - t.Error("Mapdata length wrong") - } - - if len(mapblock.Mapdata.Param2) != 4096 { - t.Error("Mapdata length wrong") - } - - if len(mapblock.Mapdata.Param1) != 4096 { - t.Error("Mapdata length wrong") - } - - pairs := mapblock.Metadata.GetPairsMap(0) - if pairs["owner"] != "pipo" { - t.Error(pairs["owner"]) - } -} - -func TestParse2(t *testing.T) { - - data, err := ioutil.ReadFile("testdata/11.0.2") - if err != nil { - t.Error(err) - } - - mapblock, err := Parse(data, 0, coords.NewMapBlockCoords(0, 0, 0)) - - if err != nil { - t.Error(err) - } - - for k, v := range mapblock.BlockMapping { - fmt.Println("Key", k, "Value", v) - } -} - -func TestParse3(t *testing.T) { - - data, err := ioutil.ReadFile("testdata/0.1.0") - if err != nil { - t.Error(err) - } - - _, err = Parse(data, 0, coords.NewMapBlockCoords(0, 0, 0)) - - if err != nil { - t.Error(err) - } -} - -func TestParseMetadata(t *testing.T) { - - data, err := ioutil.ReadFile("testdata/mb-with-metadata.bin") - if err != nil { - t.Error(err) - } - - mb, err := Parse(data, 0, coords.NewMapBlockCoords(0, 0, 0)) - - if err != nil { - t.Error(err) - } - - str, err := json.MarshalIndent(mb, "", " ") - fmt.Println(string(str)) -} diff --git a/mapblockparser/prometheus.go b/mapblockparser/prometheus.go deleted file mode 100644 index 30c147a..0000000 --- a/mapblockparser/prometheus.go +++ /dev/null @@ -1,24 +0,0 @@ -package mapblockparser - -import ( - "github.com/prometheus/client_golang/prometheus" -) - -var ( - parsedMapBlocks = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "mapblocks_parsed_count", - Help: "Overall count of parsed mapblocks", - }, - ) - parseDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "mapblock_parse_time", - Help: "Histogram for mapblock parse timings", - Buckets: prometheus.LinearBuckets(0.001, 0.002, 10), - }) -) - -func init() { - prometheus.MustRegister(parsedMapBlocks) - prometheus.MustRegister(parseDuration) -} diff --git a/mapblockparser/testdata/0.-1.0 b/mapblockparser/testdata/0.-1.0 deleted file mode 100644 index c3dbcdf0fc188d20ce30daacc4f2421eed3fa0f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1295 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 diff --git a/mapblockparser/testdata/0.2.0 b/mapblockparser/testdata/0.2.0 deleted file mode 100644 index 84b5a1c464a06da6eb84004426f2f5909f05aafc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 653 zcmb1P`v0GasbbFC>9Kju1_CYYhrAD(v3J}O4%zGYd&>2D|E;z%HeWv|(5kYaDs9UA z{NqA8$M_fn7Rb~(U6nkXu6n7UrM&X#WiLJVGfy9O{H&3hcz}56tNeJ$`oYY&DAm+W|37HyE?-%b zTW?wQFaBBm>hHVizn!=I@BU%h@BHd>KbL3DT850?e_>ranX#ZT_;z0_!*M5RQKoB# zt7pufuyEEcKFQMBFvZ7ze(FiwkDpXuucCb7tzhrZjvGgMHbmC%J9Z=|$;@!};*Ew; z*~QYgSFXJO>X_ESbq^%w#V-8#`Q;5UDUY_4J=1bsd;hL2ygqN!uBoxVn{t1DNL5BvM*-1NJ4^G%Oe zC#J3WHUDr#EmnVY`xAC1*azh?RA%lsAlllpf|>~Qv!23c+q*FYG`y)Ivt z_UTc@oaCg0L1vZ0gcyQ=f>37Hw2%I-8_@!qbOM`q=Z3S##z_Oqu_F%8c-- zGfo~*m@s+P^tefa@l$5ao-uR!%;|iW5;7aIc$m$<&N;H7zbcK9L2G)r6$20)tu19> zU}a!o;7v(QODxSPu_`Xf&r4-sW)LebNz5!N2J(Sad~sfWc~VY(ayA1K12a$ns8z6_ aD8D2%xg@{H3d&+&WDtZK1Jl68zyJWzNjDY% diff --git a/mapblockparser/testdata/0.3.0 b/mapblockparser/testdata/0.3.0 deleted file mode 100644 index 64b3bc59179e3663c4c71acf63630e4a56fec8d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 zcmb1P`v0GasbbFC8wdHC6+~PeuRmS*|Nr)-2~m&tWzMqRY^~SH-YZ z9d9;<_`jW1mG@nBdvx|*tqs?ewj7`MJUMQD_l8eF%v9d#!laf9kE=cdoIY1+G*J+{%4cJo9?CKv>-7iC1gWO15uXrnvok zpv3Lk`1Xyl&#N`ItatzTYs#IeHy%%YUe{8(BmRFq$mD(R8JVSTKdzXQ4CF8ZArP#7 RdKtuKU|>$nEaGBd006a!OltrD diff --git a/mapblockparser/testdata/0.6.0 b/mapblockparser/testdata/0.6.0 deleted file mode 100644 index ae4e38efb0760ce5fd706cf39462674d2e327031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmb1P`2U}YsbbFC8-{#`6$D%zuQwk3zCV}a#y&?8*ZO6T0+rS}+VL?!z=Qj9AJ_D( z61<+hG4%b^n_ItJtIGK9y|iq0df<5*malRx-)8-ceD+nUW$O8yl+yWkr>p_u5wER;hjG^{}Gwq<-u5pE+I^?Em*4WOU7a>2tGhU#ysu4CF8ZArLGw R+6ZDZFfb=(7I85!004LROsN0> diff --git a/mapblockparser/testdata/0.7.0 b/mapblockparser/testdata/0.7.0 deleted file mode 100644 index f82c4a5d9bd9bee3de0b3c47f2cc472c27c1ebfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmb1P`2U}YsbbFC8{T})20X5T$9m_!tKZ)G;ih9~==(W=dd}W$9l6X55b*DA^*;H{ z)yKYC&Dpiec=vRv#ZJ5PR@#1xlripnT`YJk`~2e>*Rut~>~>GQx;4F|f8#dA?bl}; zY=8Yn=2%($_k|bU{r1`;r&_N5ZO`Xf?h)<3zy4*Rut~>~>GQx;4F|f8#dA?bl}; zY=8Yn=2%($_k|bU{r1`;r&_N5ZO`Xf?h)<3zy49 zuB&=ZQo({gt^ZuLPwV{Wntk)kpBK*gq-go=Z^g;w3HP_fzLL#(y(@FC$lOeJ@+^JdW*u{Gozpot)g?_t&R)-QPD|PU;m=Ns(r(TqtqxWBT?Y}Pie?tZL z!r4#t9M|`keqXlk_{`7OpO^i!dNF4|pVjmA&F6l;UeH`GTl!)BV!nT+R}S8PeJXd) zx;tt*_ncm&?exCzI{EJ2m7x*l+dii&d_hH5(iO@)l0MC@pZf=_JMp8KllkfCO>y$C z-}$b6eZTL2^{CB_0g42!f4?hjxCG*2BrY?2!rw4%V!) zru;eIXR%MKKFj=eU8TeJXA1>CDCoSHb-+>Mh1|yGfR_>0_qXx=OMhH%@~`^we$#*1 zkN2DWD}HQm`Y-vhy~)2TOZWfz@}l|wUa1YPbpiV~ynFv|{uTHCmU1`h``EwDf86-@ zc|`&ycg_F9i}Tq3eiZ!q^w-r7ITq4=ZF>{5Bc!Lb?M%#)kZ$vTru^U7W0J~G{U6gx uuSKT+b$t!YTTi?{$?j^~`LJS6G6MqxBM<_?ni6Xen}LBjF|&w^fdK&Rp8+TU diff --git a/mapblockparser/testdata/11.0.2 b/mapblockparser/testdata/11.0.2 deleted file mode 100644 index 4b8626b52cddbccc729a4a28f2eeff866a3183ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6285 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