Files
hnh-map/internal/app/services/map.go
Nikolay Tatarinov 5ffa10f8b7 Update project structure and enhance frontend functionality
- Added a new AGENTS.md file to document the project structure and conventions.
- Updated .gitignore to include node_modules and refined cursor rules.
- Introduced new backend and frontend components for improved map interactions, including context menus and controls.
- Enhanced API composables for better admin and authentication functionalities.
- Refactored existing components for cleaner code and improved user experience.
- Updated README.md to clarify production asset serving and user setup instructions.
2026-02-25 16:32:55 +03:00

175 lines
4.2 KiB
Go

package services
import (
"encoding/json"
"strconv"
"github.com/andyleap/hnh-map/internal/app"
"github.com/andyleap/hnh-map/internal/app/store"
"go.etcd.io/bbolt"
)
// MapService handles map, markers, grids, tiles business logic.
type MapService struct {
st *store.Store
gridStorage string
gridUpdates *app.Topic[app.TileData]
mergeUpdates *app.Topic[app.Merge]
getChars func() []app.Character
}
// MapServiceDeps holds dependencies for MapService.
type MapServiceDeps struct {
Store *store.Store
GridStorage string
GridUpdates *app.Topic[app.TileData]
MergeUpdates *app.Topic[app.Merge]
GetChars func() []app.Character
}
// NewMapService creates a MapService.
func NewMapService(d MapServiceDeps) *MapService {
return &MapService{
st: d.Store,
gridStorage: d.GridStorage,
gridUpdates: d.GridUpdates,
mergeUpdates: d.MergeUpdates,
getChars: d.GetChars,
}
}
// GridStorage returns the grid storage path.
func (s *MapService) GridStorage() string {
return s.gridStorage
}
// GetCharacters returns all characters (from in-memory map).
func (s *MapService) GetCharacters() []app.Character {
if s.getChars == nil {
return nil
}
return s.getChars()
}
// GetMarkers returns all markers as FrontendMarker list.
func (s *MapService) GetMarkers() ([]app.FrontendMarker, error) {
var markers []app.FrontendMarker
err := s.st.View(func(tx *bbolt.Tx) error {
grid := s.st.GetMarkersGridBucket(tx)
if grid == nil {
return nil
}
grids := tx.Bucket(store.BucketGrids)
if grids == nil {
return nil
}
return grid.ForEach(func(k, v []byte) error {
marker := app.Marker{}
json.Unmarshal(v, &marker)
graw := grids.Get([]byte(marker.GridID))
if graw == nil {
return nil
}
g := app.GridData{}
json.Unmarshal(graw, &g)
markers = append(markers, app.FrontendMarker{
Image: marker.Image,
Hidden: marker.Hidden,
ID: marker.ID,
Name: marker.Name,
Map: g.Map,
Position: app.Position{
X: marker.Position.X + g.Coord.X*100,
Y: marker.Position.Y + g.Coord.Y*100,
},
})
return nil
})
})
return markers, err
}
// GetMaps returns maps, optionally filtering hidden for non-admin.
func (s *MapService) GetMaps(showHidden bool) (map[int]*app.MapInfo, error) {
maps := make(map[int]*app.MapInfo)
err := s.st.View(func(tx *bbolt.Tx) error {
return s.st.ForEachMap(tx, func(k, v []byte) error {
mapid, err := strconv.Atoi(string(k))
if err != nil {
return nil
}
mi := &app.MapInfo{}
json.Unmarshal(v, mi)
if mi.Hidden && !showHidden {
return nil
}
maps[mapid] = mi
return nil
})
})
return maps, err
}
// GetConfig returns config (title) and auths for session.
func (s *MapService) GetConfig(auths app.Auths) (app.Config, error) {
config := app.Config{Auths: auths}
err := s.st.View(func(tx *bbolt.Tx) error {
title := s.st.GetConfig(tx, "title")
if title != nil {
config.Title = string(title)
}
return nil
})
return config, err
}
// GetPage returns page title.
func (s *MapService) GetPage() (app.Page, error) {
p := app.Page{}
err := s.st.View(func(tx *bbolt.Tx) error {
title := s.st.GetConfig(tx, "title")
if title != nil {
p.Title = string(title)
}
return nil
})
return p, err
}
// GetGrid returns GridData by ID.
func (s *MapService) GetGrid(id string) (*app.GridData, error) {
var gd *app.GridData
err := s.st.View(func(tx *bbolt.Tx) error {
raw := s.st.GetGrid(tx, id)
if raw == nil {
return nil
}
gd = &app.GridData{}
return json.Unmarshal(raw, gd)
})
return gd, err
}
// GetTile returns TileData for map/zoom/coord.
func (s *MapService) GetTile(mapID int, c app.Coord, zoom int) *app.TileData {
var td *app.TileData
s.st.View(func(tx *bbolt.Tx) error {
raw := s.st.GetTile(tx, mapID, zoom, c.Name())
if raw != nil {
td = &app.TileData{}
json.Unmarshal(raw, td)
}
return nil
})
return td
}
// ReportMerge sends a merge event.
func (s *MapService) ReportMerge(from, to int, shift app.Coord) {
s.mergeUpdates.Send(&app.Merge{
From: from,
To: to,
Shift: shift,
})
}