diff --git a/config.go b/config.go index 874c876..21bf2a1 100644 --- a/config.go +++ b/config.go @@ -43,6 +43,21 @@ type Config struct { DropCSMRF bool Groups map[string][]string UserGroups map[string]string + List struct { + Enable bool + Addr string + Interval int + + Name string + Desc string + URL string + Creative bool + Dmg bool + PvP bool + Game string + FarNames bool + Mods []string + } } // Conf returns a copy of the Config used by the proxy. diff --git a/list.go b/list.go new file mode 100644 index 0000000..bffee25 --- /dev/null +++ b/list.go @@ -0,0 +1,117 @@ +package proxy + +import ( + "bytes" + "encoding/json" + "log" + "math" + "mime/multipart" + "net" + "net/http" + "net/textproto" + "sync" + "time" +) + +const ( + listAdd = "start" + listUpdate = "update" + listRm = "delete" +) + +var announceMu sync.Mutex + +func announce(action string) error { + announceMu.Lock() + defer announceMu.Unlock() + + addr, err := net.ResolveUDPAddr("udp", Conf().BindAddr) + if err != nil { + return err + } + + a := map[string]interface{}{ + "action": action, + "address": addr.IP.String(), + "port": addr.Port, + } + + if action != listRm { + a["name"] = Conf().List.Name + a["description"] = Conf().List.Desc + a["version"] = versionString + a["proto_min"] = latestProtoVer + a["proto_max"] = latestProtoVer + a["url"] = Conf().List.URL + a["creative"] = Conf().List.Creative + a["damage"] = Conf().List.Dmg + a["password"] = Conf().RequirePasswd + a["pvp"] = Conf().List.PvP + a["uptime"] = math.Floor(Uptime().Seconds()) + a["game_time"] = 0 + + playersMu.RLock() + a["clients"] = len(players) + clts := make([]string, 0, len(players)) + for player := range players { + clts = append(clts, player) + } + playersMu.RUnlock() + + a["clients_max"] = Conf().UserLimit + a["clients_list"] = clts + a["gameid"] = Conf().List.Game + } + + if action == listAdd { + a["can_see_far_names"] = Conf().List.FarNames + a["mods"] = Conf().List.Mods + } + + s, err := json.Marshal(a) + if err != nil { + return err + } + + body := &bytes.Buffer{} + w := multipart.NewWriter(body) + + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", "form-data; name=\"json\"") + + part, _ := w.CreatePart(header) + part.Write(s) + w.Close() + + _, err = http.Post(Conf().List.Addr+"/announce", "multipart/form-data; boundary="+w.Boundary(), body) + if err != nil { + return err + } + + log.Print("{←|⇶} announce ", action) + return nil +} + +func init() { + if Conf().List.Enable { + go func() { + var added bool + t := time.NewTicker(time.Duration(Conf().List.Interval) * time.Second) + for { + <-t.C + if !added { + if err := announce(listAdd); err != nil { + log.Print("{←|⇶} ", err) + } + + added = true + continue + } + + if err := announce(listUpdate); err != nil { + log.Print("{←|⇶} ", err) + } + } + }() + } +} diff --git a/proxy.go b/proxy.go index d7a03cc..c3da9d4 100644 --- a/proxy.go +++ b/proxy.go @@ -6,6 +6,7 @@ package proxy const latestSerializeVer = 28 const latestProtoVer = 39 +const versionString = "5.5.0-dev-83a7b48bb" const maxPlayerNameLen = 20 const playerNameChars = "^[a-zA-Z0-9-_]+$" const bytesPerMediaBunch = 5000 diff --git a/run.go b/run.go index 1a86a77..a11ffdb 100644 --- a/run.go +++ b/run.go @@ -49,6 +49,12 @@ func Run() { signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP) <-sig + if Conf().List.Enable { + if err := announce(listRm); err != nil { + log.Print("{←|⇶} ", err) + } + } + clts := l.clients() var wg sync.WaitGroup diff --git a/uptime.go b/uptime.go new file mode 100644 index 0000000..bbb085b --- /dev/null +++ b/uptime.go @@ -0,0 +1,10 @@ +package proxy + +import "time" + +var startTime = time.Now() + +// Uptime returns the time the proxy has been running for +func Uptime() time.Duration { + return time.Since(startTime) +}