1 package battlemap // import "vimagination.zapto.org/battlemap" 2 3 import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "golang.org/x/net/websocket" 9 ) 10 11 // Battlemap contains all of the data required for a battlemap system. 12 // 13 // This type implements the http.Handler interface so that it can be easily 14 // added to an existing server. 15 type Battlemap struct { 16 config config 17 socket socket 18 auth Auth 19 images assetsDir 20 audio assetsDir 21 musicPacks musicPacksDir 22 chars charactersDir 23 maps mapsDir 24 plugins pluginsDir 25 mux http.ServeMux 26 } 27 28 // New creates a new, initialised Battlemap type, using the given path as its 29 // datastore directory. 30 // 31 // The passed Auth module will be used for authenticating all users and setting 32 // Admin mode. If nil is passed then it will use the built in auth module, 33 // allowing all users as guests and allowing signing in as the Admin. 34 func New(path string, auth Auth) (*Battlemap, error) { 35 b := new(Battlemap) 36 if err := b.initModules(path, auth); err != nil { 37 return nil, err 38 } 39 b.initMux(index) 40 return b, nil 41 } 42 43 func (b *Battlemap) initModules(path string, a Auth) error { 44 if err := b.config.Init(path); err != nil { 45 return fmt.Errorf("error loading Config: %w", err) 46 } 47 b.images.fileType = fileTypeImage 48 b.audio.fileType = fileTypeAudio 49 if a == nil { 50 a := new(auth) 51 if err := a.Init(b); err != nil { 52 return fmt.Errorf(moduleError, "auth", err) 53 } 54 b.auth = a 55 } else { 56 b.auth = a 57 } 58 l := newLinks() 59 for _, m := range [...]struct { 60 Name string 61 Module interface { 62 Init(b *Battlemap, l links) error 63 } 64 }{ 65 {"Socket", &b.socket}, 66 {"Audio", &b.audio}, 67 {"MusicPacks", &b.musicPacks}, 68 {"Images", &b.images}, 69 {"Chars", &b.chars}, 70 {"Maps", &b.maps}, 71 {"Plugins", &b.plugins}, 72 } { 73 if err := m.Module.Init(b, l); err != nil { 74 return fmt.Errorf(moduleError, m.Name, err) 75 } 76 } 77 b.chars.cleanup(l.chars) 78 b.images.cleanup(l.images) 79 b.audio.cleanup(l.audio) 80 b.musicPacks.cleanup(l.music) 81 return nil 82 } 83 84 func (b *Battlemap) initMux(index http.Handler) { 85 b.mux.Handle("/socket", websocket.Handler(b.socket.ServeConn)) 86 for path, module := range map[string]http.Handler{ 87 "/login/": b.auth, 88 "/images/": &b.images, 89 "/audio/": &b.audio, 90 "/plugins/": &b.plugins, 91 } { 92 p := strings.TrimSuffix(path, "/") 93 b.mux.Handle(path, http.StripPrefix(path, module)) 94 b.mux.Handle(p, http.StripPrefix(p, module)) 95 } 96 b.mux.Handle("/", index) 97 } 98 99 func (b *Battlemap) ServeHTTP(w http.ResponseWriter, r *http.Request) { 100 b.mux.ServeHTTP(w, b.auth.Auth(r)) 101 } 102 103 const moduleError = "error initialising %s module: %w" 104