package app import ( "encoding/json" "fmt" "image" "image/png" "io" "log" "net/http" "os" "path/filepath" "strconv" "strings" "time" "go.etcd.io/bbolt" "golang.org/x/image/draw" ) type GridUpdate struct { Grids [][]string `json:"grids"` } type GridRequest struct { GridRequests []string `json:"gridRequests"` Map int `json:"map"` Coords Coord `json:"coords"` } type ExtraData struct { Season int } func (a *App) gridUpdate(rw http.ResponseWriter, req *http.Request) { defer req.Body.Close() dec := json.NewDecoder(req.Body) grup := GridUpdate{} err := dec.Decode(&grup) if err != nil { log.Println("Error decoding grid request json: ", err) http.Error(rw, "Error decoding request", http.StatusBadRequest) return } log.Println(grup) ops := []struct { mapid int x, y int f string }{} greq := GridRequest{} 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 } mapB, err := tx.CreateBucketIfNotExists([]byte("maps")) if err != nil { return err } configb, err := tx.CreateBucketIfNotExists([]byte("config")) if err != nil { return err } maps := map[int]struct{ X, Y int }{} for x, row := range grup.Grids { for y, grid := range row { gridRaw := grids.Get([]byte(grid)) if gridRaw != nil { gd := GridData{} json.Unmarshal(gridRaw, &gd) maps[gd.Map] = struct{ X, Y int }{gd.Coord.X - x, gd.Coord.Y - 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 } log.Println("Client made mapid ", seq) for x, row := range grup.Grids { for y, grid := range row { cur := GridData{} cur.ID = grid cur.Map = int(seq) cur.Coord.X = x - 1 cur.Coord.Y = y - 1 raw, err := json.Marshal(cur) if err != nil { return err } grids.Put([]byte(grid), raw) greq.GridRequests = append(greq.GridRequests, grid) } } greq.Coords = Coord{0, 0} return nil } 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 } } log.Println("Client in mapid ", mapid) for x, row := range grup.Grids { for y, grid := range row { cur := GridData{} if curRaw := grids.Get([]byte(grid)); curRaw != nil { json.Unmarshal(curRaw, &cur) if time.Now().After(cur.NextUpdate) { greq.GridRequests = append(greq.GridRequests, grid) } continue } cur.ID = grid cur.Map = mapid cur.Coord.X = x + offset.X cur.Coord.Y = y + offset.Y raw, err := json.Marshal(cur) if err != nil { return err } grids.Put([]byte(grid), raw) greq.GridRequests = append(greq.GridRequests, grid) } } if len(grup.Grids) >= 2 && len(grup.Grids[1]) >= 2 { if curRaw := grids.Get([]byte(grup.Grids[1][1])); curRaw != nil { cur := GridData{} json.Unmarshal(curRaw, &cur) greq.Map = cur.Map greq.Coords = cur.Coord } } 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}) } return nil }) if err != nil { log.Println(err) return } needProcess := map[zoomproc]struct{}{} for _, op := range ops { a.SaveTile(op.mapid, Coord{X: op.x, Y: op.y}, 0, op.f, time.Now().UnixNano()) needProcess[zoomproc{c: Coord{X: op.x, Y: op.y}.Parent(), m: op.mapid}] = struct{}{} } for z := 1; z <= 5; z++ { process := needProcess needProcess = map[zoomproc]struct{}{} for p := range process { a.updateZoomLevel(p.m, p.c, z) needProcess[zoomproc{p.c.Parent(), p.m}] = struct{}{} } } log.Println(greq) json.NewEncoder(rw).Encode(greq) } func (a *App) gridUpload(rw http.ResponseWriter, req *http.Request) { if strings.Count(req.Header.Get("Content-Type"), "=") >= 2 && strings.Count(req.Header.Get("Content-Type"), "\"") == 0 { parts := strings.SplitN(req.Header.Get("Content-Type"), "=", 2) req.Header.Set("Content-Type", parts[0]+"=\""+parts[1]+"\"") } err := req.ParseMultipartForm(100000000) if err != nil { log.Println(err) return } id := req.FormValue("id") extraData := req.FormValue("extraData") if extraData != "" { ed := ExtraData{} json.Unmarshal([]byte(extraData), &ed) if ed.Season == 3 { needTile := false a.db.Update(func(tx *bbolt.Tx) error { b, err := tx.CreateBucketIfNotExists([]byte("grids")) if err != nil { return err } curRaw := b.Get([]byte(id)) if curRaw == nil { return fmt.Errorf("Unknown grid id: %s", id) } cur := GridData{} err = json.Unmarshal(curRaw, &cur) if err != nil { return err } tiles, err := tx.CreateBucketIfNotExists([]byte("tiles")) if err != nil { return err } maps, err := tiles.CreateBucketIfNotExists([]byte(strconv.Itoa(cur.Map))) if err != nil { return err } zooms, err := maps.CreateBucketIfNotExists([]byte("0")) if err != nil { return err } tdRaw := zooms.Get([]byte(cur.Coord.Name())) if tdRaw == nil { needTile = true return nil } td := TileData{} err = json.Unmarshal(tdRaw, &td) if err != nil { return err } if td.File == "" { needTile = true return nil } if time.Now().After(cur.NextUpdate) { cur.NextUpdate = time.Now().Add(time.Minute * 30) } raw, err := json.Marshal(cur) if err != nil { return err } b.Put([]byte(id), raw) return nil }) if !needTile { log.Println("ignoring tile upload: winter") return } else { log.Println("Missing tile, using winter version") } } } file, _, err := req.FormFile("file") if err != nil { log.Println(err) return } log.Println("map tile for ", id) updateTile := false cur := GridData{} mapid := 0 a.db.Update(func(tx *bbolt.Tx) error { b, err := tx.CreateBucketIfNotExists([]byte("grids")) if err != nil { return err } curRaw := b.Get([]byte(id)) if curRaw == nil { return fmt.Errorf("Unknown grid id: %s", id) } err = json.Unmarshal(curRaw, &cur) if err != nil { return err } updateTile = time.Now().After(cur.NextUpdate) mapid = cur.Map if updateTile { cur.NextUpdate = time.Now().Add(time.Minute * 30) } raw, err := json.Marshal(cur) if err != nil { return err } b.Put([]byte(id), raw) return nil }) if updateTile { os.MkdirAll(fmt.Sprintf("%s/grids", a.gridStorage), 0600) f, err := os.Create(fmt.Sprintf("%s/grids/%s.png", a.gridStorage, cur.ID)) if err != nil { return } _, err = io.Copy(f, file) if err != nil { f.Close() return } f.Close() a.SaveTile(mapid, cur.Coord, 0, fmt.Sprintf("grids/%s.png", cur.ID), time.Now().UnixNano()) c := cur.Coord for z := 1; z <= 5; z++ { c = c.Parent() a.updateZoomLevel(mapid, c, z) } } } func (a *App) updateZoomLevel(mapid int, c Coord, z int) { img := image.NewNRGBA(image.Rect(0, 0, 100, 100)) draw.Draw(img, img.Bounds(), image.Transparent, image.Point{}, draw.Src) for x := 0; x <= 1; x++ { for y := 0; y <= 1; y++ { subC := c subC.X *= 2 subC.Y *= 2 subC.X += x subC.Y += y td := a.GetTile(mapid, subC, z-1) if td == nil || td.File == "" { continue } subf, err := os.Open(filepath.Join(a.gridStorage, td.File)) if err != nil { continue } subimg, _, err := image.Decode(subf) subf.Close() if err != nil { continue } draw.BiLinear.Scale(img, image.Rect(50*x, 50*y, 50*x+50, 50*y+50), subimg, subimg.Bounds(), draw.Src, nil) } } os.MkdirAll(fmt.Sprintf("%s/%d/%d", a.gridStorage, mapid, z), 0600) f, err := os.Create(fmt.Sprintf("%s/%d/%d/%s.png", a.gridStorage, mapid, z, c.Name())) a.SaveTile(mapid, c, z, fmt.Sprintf("%d/%d/%s.png", mapid, z, c.Name()), time.Now().UnixNano()) if err != nil { return } defer func() { f.Close() }() png.Encode(f, img) }