Update project documentation and improve frontend functionality
- Updated the backend documentation in CONTRIBUTING.md and README.md to reflect changes in application structure and API endpoints. - Enhanced the frontend components in MapView.vue for better handling of context menu actions. - Added new types and interfaces in TypeScript for improved type safety in the frontend. - Introduced new utility classes for managing characters and markers in the map. - Updated .gitignore to include .vscode directory for better development environment management.
This commit is contained in:
305
internal/app/admin_merge.go
Normal file
305
internal/app/admin_merge.go
Normal file
@@ -0,0 +1,305 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user