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 37 if err := b.initModules(path, auth); err != nil { 38 return nil, err 39 } 40 41 b.initMux(index) 42 43 return b, nil 44 } 45 46 func (b *Battlemap) initModules(path string, a Auth) error { 47 if err := b.config.Init(path); err != nil { 48 return fmt.Errorf("error loading Config: %w", err) 49 } 50 51 b.images.fileType = fileTypeImage 52 b.audio.fileType = fileTypeAudio 53 54 if a == nil { 55 a := new(auth) 56 57 if err := a.Init(b); err != nil { 58 return fmt.Errorf(moduleError, "auth", err) 59 } 60 61 b.auth = a 62 } else { 63 b.auth = a 64 } 65 66 l := newLinks() 67 68 for _, m := range [...]struct { 69 Name string 70 Module interface { 71 Init(b *Battlemap, l links) error 72 } 73 }{ 74 {"Socket", &b.socket}, 75 {"Audio", &b.audio}, 76 {"MusicPacks", &b.musicPacks}, 77 {"Images", &b.images}, 78 {"Chars", &b.chars}, 79 {"Maps", &b.maps}, 80 {"Plugins", &b.plugins}, 81 } { 82 if err := m.Module.Init(b, l); err != nil { 83 return fmt.Errorf(moduleError, m.Name, err) 84 } 85 } 86 87 b.chars.cleanup(l.chars) 88 b.images.cleanup(l.images) 89 b.audio.cleanup(l.audio) 90 b.musicPacks.cleanup(l.music) 91 92 return nil 93 } 94 95 func (b *Battlemap) initMux(index http.Handler) { 96 b.mux.Handle("/socket", websocket.Handler(b.socket.ServeConn)) 97 98 for path, module := range map[string]http.Handler{ 99 "/login/": b.auth, 100 "/images/": &b.images, 101 "/audio/": &b.audio, 102 "/plugins/": &b.plugins, 103 } { 104 p := strings.TrimSuffix(path, "/") 105 106 b.mux.Handle(path, http.StripPrefix(path, module)) 107 b.mux.Handle(p, http.StripPrefix(p, module)) 108 } 109 110 b.mux.Handle("/", index) 111 } 112 113 func (b *Battlemap) ServeHTTP(w http.ResponseWriter, r *http.Request) { 114 b.mux.ServeHTTP(w, b.auth.Auth(r)) 115 } 116 117 const moduleError = "error initialising %s module: %w" 118