func generate(r *byteio.StickyReader, w *byteio.StickyWriter, of *os.File) error { memoryLimit := r.ReadUint64() size := r.ReadInt64() gPath := data.ReadString(r) levelName := data.ReadString(r) mapPath := data.ReadString(r) if r.Err != nil { return r.Err } o, err := ora.Open(of, size) if err != nil { return err } f, err := os.Open(path.Join(gPath, "data.gen")) if err != nil { return err } g, err := LoadGenerator(f) if e := f.Close(); e != nil { return e } if err != nil { return err } b := o.Bounds() w.WriteUint8(2) w.WriteInt32(int32(b.Max.X) >> 4) w.WriteInt32(int32(b.Max.Y) >> 4) c := make(chan paint, 1024) m := make(chan string, 4) e := make(chan struct{}, 0) go func() { defer close(e) defer close(m) defer close(c) for { select { case message := <-m: w.WriteUint8(3) data.WriteString(w, message) case p := <-c: w.WriteUint8(4) w.WriteInt32(p.X) w.WriteInt32(p.Y) r, g, b, a := p.RGBA() w.WriteUint8(uint8(r >> 8)) w.WriteUint8(uint8(g >> 8)) w.WriteUint8(uint8(b >> 8)) w.WriteUint8(uint8(a >> 8)) case <-e: return } } }() err = g.Generate(levelName, mapPath, o, c, m, memoryLimit) e <- struct{}{} <-e return err }
func transferFile(typeName, method string, typeID uint8, o *overlay.Overlay) dom.Node { name := xform.InputText("name", "") url := xform.InputRadio("url", "switch", true) upload := xform.InputRadio("upload", "switch", false) fileI := xform.InputUpload("") urlI := xform.InputURL("", "") s := xform.InputSubmit(method) name.Required = true typeFunc := func(dom.Event) { if url.Checked { urlI.Style().RemoveProperty("display") fileI.Style().SetProperty("display", "none", "") urlI.Required = true fileI.Required = false fileI.SetID("") urlI.SetID("file") } else { fileI.Style().RemoveProperty("display") urlI.Style().SetProperty("display", "none", "") fileI.Required = true urlI.Required = false urlI.SetID("") fileI.SetID("file") } } typeFunc(nil) url.AddEventListener("change", false, typeFunc) upload.AddEventListener("change", false, typeFunc) f := xjs.AppendChildren(xdom.Form(), xjs.AppendChildren(xdom.Fieldset(), xjs.SetInnerText(xdom.Legend(), method+" "+typeName), xform.Label(typeName+" Name", "name"), name, xdom.Br(), xform.Label("URL", "url"), url, xdom.Br(), xform.Label("Upload", "upload"), upload, xdom.Br(), xform.Label("File", "file"), fileI, urlI, xdom.Br(), s, )) s.AddEventListener("click", false, func(e dom.Event) { if name.Value == "" { return } if url.Checked { if urlI.Value == "" { return } } else if len(fileI.Files()) != 1 { return } s.Disabled = true name.Disabled = true url.Disabled = true upload.Disabled = true fileI.Disabled = true urlI.Disabled = true e.PreventDefault() go func() { d := xdom.Div() uo := overlay.New(d) uo.OnClose(func() { o.Close() }) xjs.Body().AppendChild(uo) status := xdom.Div() d.AppendChild(xjs.SetInnerText(status, "Transferring...")) conn, err := websocket.Dial("ws://" + js.Global.Get("location").Get("host").String() + "/transfer") if err != nil { xjs.SetInnerText(status, err.Error()) return } defer conn.Close() w := byteio.StickyWriter{Writer: byteio.LittleEndianWriter{Writer: conn}} r := byteio.StickyReader{Reader: byteio.LittleEndianReader{Reader: conn}} pb := progress.New(color.RGBA{255, 0, 0, 0}, color.RGBA{0, 0, 255, 0}, 400, 50) d.AppendChild(pb) if url.Checked { w.WriteUint8(typeID << 1) data.WriteString(&w, urlI.Value) length := int(r.ReadInt32()) total := 0 for total < length { switch v := r.ReadUint8(); v { case 1: i := int(r.ReadInt32()) total += i pb.Percent(100 * total / length) default: xjs.SetInnerText(status, ReadError(&r).Error()) return } } } else { f := files.NewFileReader(files.NewFile(fileI.Files()[0])) l := f.Len() if l == 0 { xjs.SetInnerText(status, "Zero-length file") return } w.WriteUint8(typeID<<1 | 1) w.WriteInt32(int32(l)) io.Copy(&w, pb.Reader(f, l)) } d.RemoveChild(pb) xjs.SetInnerText(status, "Checking File") data.WriteString(&w, name.Value) var ctx *dom.CanvasRenderingContext2D for { switch v := r.ReadUint8(); v { case 0: if r.Err != nil { xjs.SetInnerText(status, r.Err.Error()) } else { xjs.SetInnerText(status, ReadError(&r).Error()) } return case 1: files := make([]xform.Option, r.ReadInt16()) for i := range files { files[i] = xform.Option{ Value: strconv.Itoa(i), Label: data.ReadString(&r), } } j := xform.SelectBox("files", files...) sel := xjs.SetInnerText(xdom.Button(), "Select") fo := overlay.New(xjs.AppendChildren(xdom.Div(), xjs.AppendChildren(xdom.Fieldset(), xjs.SetInnerText(xdom.Legend(), "Please select the "+typeName+" file"), xform.Label("File", "files"), j, xdom.Br(), sel, ))) c := make(chan int16, 0) done := false fo.OnClose(func() { if !done { done = true c <- -1 } }) sel.AddEventListener("click", false, func(dom.Event) { if !done { done = true v, err := strconv.Atoi(j.Value) if err != nil { v = -1 } c <- int16(v) fo.Close() } }) xjs.Body().AppendChild(fo) w.WriteInt16(<-c) close(c) case 2: w := r.ReadInt32() h := r.ReadInt32() canvas := xdom.Canvas() canvas.Width = int(w) * 8 canvas.Height = int(h) * 8 d.AppendChild(canvas) ctx = canvas.GetContext2d() ctx.Scale(8, 8) case 3: xjs.SetInnerText(status, data.ReadString(&r)) case 4: x := r.ReadInt32() y := r.ReadInt32() red := r.ReadUint8() green := r.ReadUint8() blue := r.ReadUint8() alpha := r.ReadUint8() ctx.FillStyle = "rgba(" + strconv.Itoa(int(red)) + ", " + strconv.Itoa(int(green)) + ", " + strconv.Itoa(int(blue)) + ", " + strconv.FormatFloat(float64(alpha)/255, 'f', -1, 32) + ")" ctx.FillRect(int(x), int(y), 1, 1) case 255: uo.Close() return } } }() }) return f }
func (c Console) handle(r *byteio.StickyReader, w *byteio.StickyWriter) error { id := int(r.ReadInt32()) if r.Err != nil { return r.Err } s := c.c.c.Server(id) if s == nil { return ErrUnknownServer } s.RLock() //Needed? Path never gets changed! p := s.Path s.RUnlock() var ( f *os.File err error logPath string ) for _, lp := range logPaths { logPath = path.Join(p, lp) f, err = os.Open(logPath) if err == nil { break } } if f == nil { return errors.New("unable to open log file") } defer func() { f.Close() }() fsw, err := fsnotify.NewWatcher() if err != nil { return err } logDir := path.Dir(logPath) fsw.Add(logDir) defer fsw.Remove(logDir) pw := partWriter{w} io.Copy(pw, f) if w.Err != nil { return w.Err } t := time.NewTimer(time.Second * 10) for { select { case ev := <-fsw.Events: switch ev.Op { case fsnotify.Create: if ev.Name == logPath { f, err = os.Open(logPath) if err != nil { return err } } case fsnotify.Write: if ev.Name == logPath { io.Copy(pw, f) if w.Err != nil { return w.Err } } case fsnotify.Remove: if ev.Name == logPath { f.Close() } } case err = <-fsw.Errors: case <-t.C: w.WriteUint8(2) //ping if w.Err != nil { return w.Err } } t.Reset(time.Second * 10) } }
func (t Transfer) server(name string, r *byteio.StickyReader, w *byteio.StickyWriter, f *os.File, size int64) error { zr, err := zip.NewReader(f, size) if err != nil { return err } jars := make([]*zip.File, 0, 16) for _, file := range zr.File { if file.Name == "server.jar" { jars = []*zip.File{file} break } if strings.HasSuffix(file.Name, ".jar") { jars = append(jars, file) } } s := t.c.NewServer() done := false defer func() { if !done { t.c.RemoveServer(s.ID) } go t.c.Save() }() if s == nil { return errors.New("error creating server") } s.Lock() s.Name = name d := s.Path s.Unlock() if len(jars) == 0 { err = moveFile(f.Name(), path.Join(d, "server.jar")) } else { if len(jars) > 1 { w.WriteUint8(1) w.WriteInt16(int16(len(jars))) for _, jar := range jars { data.WriteString(w, jar.Name) } if w.Err != nil { return w.Err } p := r.ReadInt16() if r.Err != nil { return r.Err } if int(p) >= len(jars) || p < 0 { return errors.New("error selecting server jar") } jars[0] = jars[p] } if err == nil { err = unzip(zr, d) if err == nil { err = os.Rename(path.Join(d, jars[0].Name), path.Join(d, "server.jar")) } } } if err != nil { return err } serverProperties := DefaultServerSettings() ps, err := os.OpenFile(path.Join(d, "properties.server"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) defer ps.Close() if err != nil { if !os.IsExist(err) { return err } } else { err = serverProperties.WriteTo(ps) if err != nil { return err } } done = true return nil }
func serverConsole(s data.Server) func(dom.Element) { return func(c dom.Element) { log := xform.TextArea("log", "") log.ReadOnly = true command := xform.InputText("command", "") command.Required = true send := xform.InputSubmit("Send") c.AppendChild(xjs.AppendChildren(xdom.Form(), xjs.AppendChildren(xdom.Fieldset(), xjs.SetInnerText(xdom.Legend(), "Console"), xform.Label("Log", ""), log, xdom.Br(), xform.Label("Command", "command"), command, send, ))) if s.State == data.StateStopped { send.Disabled = true command.Disabled = true } else { send.AddEventListener("click", false, func(e dom.Event) { if command.Value == "" { return } e.PreventDefault() send.Disabled = true cmd := command.Value log.Value += "\n>" + cmd + "\n" log.Set("scrollTop", log.Get("scrollHeight")) command.Value = "" go func() { err := RPC.WriteCommand(s.ID, cmd) if err != nil { xjs.Alert("Error sending command: %s", err) return } send.Disabled = false }() }) } go func() { conn, err := websocket.Dial("ws://" + js.Global.Get("location").Get("host").String() + "/console") if err != nil { xjs.Alert("Failed to connect to console: %s", err) return } defer conn.Close() w := byteio.StickyWriter{Writer: byteio.LittleEndianWriter{Writer: conn}} r := byteio.StickyReader{Reader: byteio.LittleEndianReader{Reader: conn}} updateStop := make(chan struct{}) registerUpdateStopper(c, updateStop) done := false go func() { <-updateStop done = true conn.Close() }() w.WriteInt32(int32(s.ID)) for { state := r.ReadUint8() switch state { case 0: if !done { err := ReadError(&r) if r.Err != nil { err = r.Err } log.Value += "\n\nError reading from console: " + err.Error() log.Set("scrollTop", log.Get("scrollHeight")) } return case 1: log.Value += data.ReadString(&r) log.Set("scrollTop", log.Get("scrollHeight")) } } }() } }
func (t Transfer) generator(name string, r *byteio.StickyReader, w *byteio.StickyWriter, f *os.File, size int64) error { g := t.c.Generators.New(t.c.Settings().DirGenerators) if g == nil { return errors.New("error creating generator") } g.Name = name done := false defer func() { if !done { t.c.RemoveGenerator(g.ID) } go t.c.Save() }() zr, err := zip.NewReader(f, size) if err != nil { f.Seek(0, 0) e := json.NewDecoder(f).Decode(new(data.GeneratorData)) if e != nil { return err } err = moveFile(f.Name(), path.Join(g.Path, "data.gen")) if err != nil { return err } done = true return nil } gens := make([]*zip.File, 0, 16) for _, file := range zr.File { if file.Name == "data.gen" { gens = []*zip.File{file} break } if strings.HasSuffix(file.Name, ".gen") || strings.HasSuffix(file.Name, ".json") { gens = append(gens, file) } } if len(gens) == 0 { return errors.New("cannot find generator data in zip") } if len(gens) > 1 { w.WriteUint8(1) w.WriteInt16(int16(len(gens))) for _, gen := range gens { data.WriteString(w, gen.Name) } if w.Err != nil { return w.Err } p := r.ReadInt16() if r.Err != nil { return r.Err } if int(p) >= len(gens) || p < 0 { return errors.New("error selecting generator data") } gens[0] = gens[p] } gd, err := gens[0].Open() if err != nil { return err } err = json.NewDecoder(gd).Decode(new(data.GeneratorData)) if err != nil { return err } err = unzip(zr, g.Path) if err != nil { return err } err = os.Rename(path.Join(g.Path, gens[0].Name), path.Join(g.Path, "data.gen")) if err != nil { return err } done = true return nil }
func (t Transfer) generate(name string, r *byteio.StickyReader, w *byteio.StickyWriter, f *os.File, size int64) error { gp.Add(1) defer gp.Done() mp := t.c.NewMap() if mp == nil { return errors.New("failed to create map") } done := false defer func() { if !done { t.c.RemoveMap(mp.ID) } t.c.Save() }() mp.Lock() mp.Name = name mapPath := mp.Path mp.Server = -2 mp.Unlock() t.c.Generators.mu.RLock() gs := make([]data.Generator, len(t.c.Generators.List)) for n, g := range t.c.Generators.List { gs[n] = *g } t.c.Generators.mu.RUnlock() var g data.Generator if len(gs) == 0 { return errors.New("no generators installed") } else if len(gs) == 1 { g = gs[0] } else { w.WriteUint8(1) w.WriteInt16(int16(len(gs))) for _, tg := range gs { data.WriteString(w, tg.Name) } if w.Err != nil { return w.Err } gID := r.ReadInt16() if r.Err != nil { return r.Err } if gID < 0 || int(gID) >= len(gs) { return errors.New("unknown generator") } g = gs[gID] } ms := DefaultMapSettings() ms["level-type"] = minecraft.FlatGenerator ms["generator-settings"] = "0" ms["motd"] = name j, err := os.Open(path.Join(g.Path, "data.gen")) if err != nil { return err } var gj data.GeneratorData err = json.NewDecoder(j).Decode(&gj) j.Close() if err != nil { return err } for k, v := range gj.Options { ms[k] = v } pf, err := os.Create(path.Join(mapPath, "properties.map")) if err != nil { return err } if err = ms.WriteTo(pf); err != nil { return err } pf.Close() cmd := exec.Command(t.c.Settings().GeneratorExecutable) cmd.ExtraFiles = append(cmd.ExtraFiles, f) cmd.Dir, err = os.Getwd() if err != nil { return err } cmd.Stdout = w pw, err := cmd.StdinPipe() if err != nil { return err } err = gp.Start(cmd) if err != nil { return err } defer gp.Remove(cmd) pww := byteio.StickyWriter{Writer: &byteio.LittleEndianWriter{pw}} pww.WriteUint64(t.c.Settings().GeneratorMaxMem) pww.WriteInt64(size) data.WriteString(&pww, g.Path) data.WriteString(&pww, name) data.WriteString(&pww, mapPath) if pww.Err != nil { return pww.Err } err = cmd.Wait() if err != nil { return err } done = true mp.Lock() mp.Server = -1 mp.Unlock() return nil }
func ReadString(r *byteio.StickyReader) string { length := r.ReadUint16() str := make([]byte, int(length)) io.ReadFull(r, str) return string(str) }