1 package battlemap 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "path/filepath" 8 "strconv" 9 "strings" 10 11 "vimagination.zapto.org/keystore" 12 ) 13 14 type mapsDir struct { 15 folders 16 maps map[uint64]*levelMap 17 handler http.Handler 18 } 19 20 func (m *mapsDir) Init(b *Battlemap, links links) error { 21 var location keystore.String 22 err := b.config.Get("MapsDir", &location) 23 if err != nil { 24 return fmt.Errorf("error getting map directory: %w", err) 25 } 26 sp := filepath.Join(b.config.BaseDir, string(location)) 27 store, err := keystore.NewFileStore(sp, sp, keystore.NoMangle) 28 if err != nil { 29 return fmt.Errorf("error creating map store: %w", err) 30 } 31 m.folders.fileType = fileTypeMap 32 if err := m.folders.Init(b, store, links.maps); err != nil { 33 return fmt.Errorf("error parsing maps keystore folders: %w", err) 34 } 35 m.maps = make(map[uint64]*levelMap, len(links.maps)) 36 for id := range links.maps { 37 key := strconv.FormatUint(id, 10) 38 mp := new(levelMap) 39 if err = m.Get(key, mp); err != nil { 40 return fmt.Errorf("error reading map data (%q): %w", key, err) 41 } 42 for key, value := range mp.Data { 43 if f := links.getLinkKey(key); f != nil { 44 f.setJSONLinks(value) 45 } 46 } 47 for _, t := range mp.tokens { 48 if t.Source > 0 { 49 links.images.setLink(t.Source) 50 } 51 for key, value := range t.TokenData { 52 if f := links.getLinkKey(key); f != nil { 53 f.setJSONLinks(value.Data) 54 } 55 } 56 } 57 m.maps[id] = mp 58 } 59 m.handler = http.FileServer(http.Dir(sp)) 60 return nil 61 } 62 63 type mapDetails struct { 64 ID uint64 `json:"id,omitempty"` 65 Name string `json:"name"` 66 mapDimensions 67 mapGrid 68 } 69 70 type mapDimensions struct { 71 Width uint64 `json:"width"` 72 Height uint64 `json:"height"` 73 } 74 75 type mapGrid struct { 76 GridType uint8 `json:"gridType"` 77 GridSize uint64 `json:"gridSize"` 78 GridColour colour `json:"gridColour"` 79 GridStroke uint64 `json:"gridStroke"` 80 } 81 82 func (m *mapsDir) newMap(nm mapDetails, id ID) (json.RawMessage, error) { 83 if nm.Width == 0 || nm.Height == 0 { 84 return nil, ErrInvalidDimensions 85 } 86 m.mu.Lock() 87 m.lastID++ 88 mid := m.lastID 89 if nm.Name == "" { 90 nm.Name = "Map " + strconv.FormatUint(mid, 10) 91 } 92 mp := &levelMap{ 93 Width: nm.Width, 94 Height: nm.Height, 95 GridSize: nm.GridSize, 96 GridColour: nm.GridColour, 97 GridStroke: nm.GridStroke, 98 layers: map[string]struct{}{ 99 "Layer": {}, 100 "Light": {}, 101 "Grid": {}, 102 }, 103 layer: layer{ 104 Layers: []*layer{ 105 { 106 Name: "Layer", 107 }, 108 { 109 Name: "Light", 110 }, 111 { 112 Name: "Grid", 113 }, 114 }, 115 }, 116 tokens: make(map[uint64]layerToken), 117 walls: make(map[uint64]layerWall), 118 Data: make(map[string]json.RawMessage), 119 } 120 name := addItemTo(m.folders.root.Items, nm.Name, mid) 121 m.maps[mid] = mp 122 m.saveFolders() 123 m.mu.Unlock() 124 m.Set(strconv.FormatUint(mid, 10), mp) 125 buf := append(appendString(append(strconv.AppendUint(append(json.RawMessage{}, "[{\"id\":"...), mid, 10), ",\"name\":"...), name), '}', ']') 126 m.socket.broadcastAdminChange(broadcastMapItemAdd, buf, id) 127 return buf[1 : len(buf)-1], nil 128 } 129 130 func (m *mapsDir) updateMapData(id uint64, fn func(*levelMap) bool) error { 131 m.mu.Lock() 132 mp, ok := m.maps[id] 133 if ok && fn(mp) { 134 m.Set(strconv.FormatUint(id, 10), mp) 135 } 136 m.mu.Unlock() 137 if !ok { 138 return ErrUnknownMap 139 } 140 return nil 141 } 142 143 type layerType uint8 144 145 const ( 146 anyLayer layerType = iota 147 anyLayerAll 148 tokenLayer 149 folderLayer 150 ) 151 152 func (m *mapsDir) updateMapLayer(mid uint64, path string, lt layerType, fn func(*levelMap, *layer) bool) error { 153 var err error 154 if errr := m.updateMapData(mid, func(mp *levelMap) bool { 155 if l := getLayer(&mp.layer, path, lt == anyLayerAll); l != nil { 156 if lt == tokenLayer && l.Layers != nil || lt == folderLayer && l.Layers == nil { 157 err = ErrInvalidLayerPath 158 return false 159 } 160 return fn(mp, l) 161 } 162 err = ErrUnknownLayer 163 return false 164 }); errr != nil { 165 return errr 166 } 167 return err 168 } 169 170 func (m *mapsDir) updateMapsLayerToken(mid uint64, id uint64, fn func(*levelMap, *layer, *token) bool) error { 171 var err error 172 if errr := m.updateMapData(mid, func(mp *levelMap) bool { 173 if tk, ok := mp.tokens[id]; ok { 174 return fn(mp, tk.layer, tk.token) 175 } 176 err = ErrUnknownToken 177 return false 178 }); errr != nil { 179 return errr 180 } 181 return err 182 } 183 184 func (m *mapsDir) ServeHTTP(w http.ResponseWriter, r *http.Request) { 185 if m.auth.IsAdmin(r) { 186 m.mu.RLock() 187 m.handler.ServeHTTP(w, r) 188 m.mu.RUnlock() 189 } else { 190 var currentUserMap keystore.Uint 191 m.config.Get("currentUserMap", ¤tUserMap) 192 id, _ := strconv.ParseUint(strings.TrimPrefix(r.URL.Path, "/"), 10, 0) 193 if id == uint64(currentUserMap) { 194 m.mu.RLock() 195 m.handler.ServeHTTP(w, r) 196 m.mu.RUnlock() 197 } else { 198 http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) 199 } 200 } 201 } 202 203 func uniqueLayer(l map[string]struct{}, name string) string { 204 return uniqueName(name, func(name string) bool { 205 if _, ok := l[name]; !ok { 206 l[name] = struct{}{} 207 return true 208 } 209 return false 210 }) 211 } 212 213 func getLayer(l *layer, p string, all bool) *layer { 214 Loop: 215 for _, p := range strings.Split(strings.TrimRight(strings.TrimLeft(p, "/"), "/"), "/") { 216 switch p { 217 case "": 218 continue 219 case "Light", "Grid": 220 if !all { 221 return nil 222 } 223 } 224 for _, m := range l.Layers { 225 if m.Name == p { 226 l = m 227 continue Loop 228 } 229 } 230 return nil 231 } 232 return l 233 } 234 235 func getParentLayer(l *layer, p string, all bool) (*layer, *layer) { 236 parentStr, name := splitAfterLastSlash(strings.TrimRight(p, "/")) 237 parent := getLayer(l, parentStr, false) 238 if parent == nil || parent.Layers == nil { 239 return nil, nil 240 } 241 return parent, getLayer(parent, name, all) 242 } 243 244 func (l *layer) removeLayer(name string) { 245 pos := -1 246 for n, m := range l.Layers { 247 if m.Name == name { 248 pos = n 249 break 250 } 251 } 252 if pos == -1 { 253 return 254 } 255 if pos < len(l.Layers)-1 { 256 copy(l.Layers[pos:], l.Layers[pos+1:]) 257 } 258 l.Layers[len(l.Layers)-1] = nil 259 l.Layers = l.Layers[:len(l.Layers)-1] 260 } 261 262 func (l *layer) addLayer(nl *layer, pos uint) { 263 if pos >= uint(len(l.Layers)) { 264 pos = uint(len(l.Layers)) 265 } 266 l.Layers = append(l.Layers, nil) 267 copy(l.Layers[pos+1:], l.Layers[pos:]) 268 l.Layers[pos] = nl 269 } 270 271 func (l *layer) removeToken(id uint64) { 272 pos := -1 273 for p, tk := range l.Tokens { 274 if tk.ID == id { 275 pos = p 276 break 277 } 278 } 279 if pos == -1 { 280 return 281 } 282 copy(l.Tokens[pos:], l.Tokens[pos+1:]) 283 l.Tokens[len(l.Tokens)-1] = nil 284 l.Tokens = l.Tokens[:len(l.Tokens)-1] 285 } 286 287 func (l *layer) addToken(tk *token, pos uint) uint { 288 if pos >= uint(len(l.Tokens)) { 289 pos = uint(len(l.Tokens)) 290 } 291 l.Tokens = append(l.Tokens, nil) 292 copy(l.Tokens[pos+1:], l.Tokens[pos:]) 293 l.Tokens[pos] = tk 294 return pos 295 } 296