package app import ( "archive/zip" "encoding/json" "fmt" "io" "log" "net/http" "os" "path/filepath" "strconv" "strings" "time" "go.etcd.io/bbolt" ) func (a *App) merge(rw http.ResponseWriter, req *http.Request) { if s := a.getSession(req); s == nil || !s.Auths.Has(AUTH_ADMIN) { http.Error(rw, "Unauthorized", http.StatusUnauthorized) return } err := req.ParseMultipartForm(1024 * 1024 * 500) if err != nil { log.Println(err) http.Error(rw, "internal error", http.StatusInternalServerError) return } mergef, hdr, err := req.FormFile("merge") if err != nil { log.Println(err) http.Error(rw, "request error", http.StatusBadRequest) return } zr, err := zip.NewReader(mergef, hdr.Size) if err != nil { log.Println(err) http.Error(rw, "request error", http.StatusBadRequest) return } ops := []struct { mapid int x, y int f string }{} newTiles := map[string]struct{}{} err = a.db.Update(func(tx *bbolt.Tx) error { grids, err := tx.CreateBucketIfNotExists([]byte("grids")) if err != nil { return err } tiles, err := tx.CreateBucketIfNotExists([]byte("tiles")) if err != nil { return err } mb, err := tx.CreateBucketIfNotExists([]byte("markers")) if err != nil { return err } mgrid, err := mb.CreateBucketIfNotExists([]byte("grid")) if err != nil { return err } idB, err := mb.CreateBucketIfNotExists([]byte("id")) if err != nil { return err } configb, err := tx.CreateBucketIfNotExists([]byte("config")) if err != nil { return err } for _, fhdr := range zr.File { if strings.HasSuffix(fhdr.Name, ".json") { f, err := fhdr.Open() if err != nil { return err } md := mapData{} err = json.NewDecoder(f).Decode(&md) if err != nil { return err } for _, ms := range md.Markers { for _, mraw := range ms { key := []byte(fmt.Sprintf("%s_%d_%d", mraw.GridID, mraw.Position.X, mraw.Position.Y)) if mgrid.Get(key) != nil { continue } if mraw.Image == "" { mraw.Image = "gfx/terobjs/mm/custom" } id, err := idB.NextSequence() if err != nil { return err } idKey := []byte(strconv.Itoa(int(id))) m := Marker{ Name: mraw.Name, ID: int(id), GridID: mraw.GridID, Position: Position{ X: mraw.Position.X, Y: mraw.Position.Y, }, Image: mraw.Image, } raw, _ := json.Marshal(m) mgrid.Put(key, raw) idB.Put(idKey, key) } } mapB, err := tx.CreateBucketIfNotExists([]byte("maps")) if err != nil { return err } newGrids := map[Coord]string{} maps := map[int]struct{ X, Y int }{} for k, v := range md.Grids { c := Coord{} _, err := fmt.Sscanf(k, "%d_%d", &c.X, &c.Y) if err != nil { return err } newGrids[c] = v gridRaw := grids.Get([]byte(v)) if gridRaw != nil { gd := GridData{} json.Unmarshal(gridRaw, &gd) maps[gd.Map] = struct{ X, Y int }{gd.Coord.X - c.X, gd.Coord.Y - c.Y} } } if len(maps) == 0 { seq, err := mapB.NextSequence() if err != nil { return err } mi := MapInfo{ ID: int(seq), Name: strconv.Itoa(int(seq)), Hidden: configb.Get([]byte("defaultHide")) != nil, } raw, _ := json.Marshal(mi) err = mapB.Put([]byte(strconv.Itoa(int(seq))), raw) if err != nil { return err } for c, grid := range newGrids { cur := GridData{} cur.ID = grid cur.Map = int(seq) cur.Coord = c raw, err := json.Marshal(cur) if err != nil { return err } grids.Put([]byte(grid), raw) } continue } mapid := -1 offset := struct{ X, Y int }{} for id, off := range maps { mi := MapInfo{} mraw := mapB.Get([]byte(strconv.Itoa(id))) if mraw != nil { json.Unmarshal(mraw, &mi) } if mi.Priority { mapid = id offset = off break } if id < mapid || mapid == -1 { mapid = id offset = off } } for c, grid := range newGrids { cur := GridData{} if curRaw := grids.Get([]byte(grid)); curRaw != nil { continue } cur.ID = grid cur.Map = mapid cur.Coord.X = c.X + offset.X cur.Coord.Y = c.Y + offset.Y raw, err := json.Marshal(cur) if err != nil { return err } grids.Put([]byte(grid), raw) } if len(maps) > 1 { grids.ForEach(func(k, v []byte) error { gd := GridData{} json.Unmarshal(v, &gd) if gd.Map == mapid { return nil } if merge, ok := maps[gd.Map]; ok { var td *TileData mapb, err := tiles.CreateBucketIfNotExists([]byte(strconv.Itoa(gd.Map))) if err != nil { return err } zoom, err := mapb.CreateBucketIfNotExists([]byte(strconv.Itoa(0))) if err != nil { return err } tileraw := zoom.Get([]byte(gd.Coord.Name())) if tileraw != nil { json.Unmarshal(tileraw, &td) } gd.Map = mapid gd.Coord.X += offset.X - merge.X gd.Coord.Y += offset.Y - merge.Y raw, _ := json.Marshal(gd) if td != nil { ops = append(ops, struct { mapid int x int y int f string }{ mapid: mapid, x: gd.Coord.X, y: gd.Coord.Y, f: td.File, }) } grids.Put(k, raw) } return nil }) } for mergeid, merge := range maps { if mapid == mergeid { continue } mapB.Delete([]byte(strconv.Itoa(mergeid))) log.Println("Reporting merge", mergeid, mapid) a.reportMerge(mergeid, mapid, Coord{X: offset.X - merge.X, Y: offset.Y - merge.Y}) } } else if strings.HasSuffix(fhdr.Name, ".png") { os.MkdirAll(filepath.Join(a.gridStorage, "grids"), 0777) f, err := os.Create(filepath.Join(a.gridStorage, "grids", filepath.Base(fhdr.Name))) if err != nil { return err } r, err := fhdr.Open() if err != nil { f.Close() return err } io.Copy(f, r) r.Close() f.Close() newTiles[strings.TrimSuffix(filepath.Base(fhdr.Name), ".png")] = struct{}{} } } for gid := range newTiles { gridRaw := grids.Get([]byte(gid)) if gridRaw != nil { gd := GridData{} json.Unmarshal(gridRaw, &gd) ops = append(ops, struct { mapid int x int y int f string }{ mapid: gd.Map, x: gd.Coord.X, y: gd.Coord.Y, f: filepath.Join("grids", gid+".png"), }) } } return nil }) if err != nil { log.Println(err) http.Error(rw, "internal error", http.StatusInternalServerError) return } for _, op := range ops { a.SaveTile(op.mapid, Coord{X: op.x, Y: op.y}, 0, op.f, time.Now().UnixNano()) } a.doRebuildZooms() }