minewebgen - transfer_generate.go
1 package main
2
3 import (
4 "encoding/json"
5 "errors"
6 "os"
7 "os/exec"
8 "path"
9 "sync"
10
11 "vimagination.zapto.org/byteio"
12 "vimagination.zapto.org/minecraft"
13 "vimagination.zapto.org/minewebgen/internal/data"
14 )
15
16 type generatorProcesses struct {
17 sync.WaitGroup
18 sync.Mutex
19 cmds []*exec.Cmd
20 }
21
22 func (g *generatorProcesses) Start(c *exec.Cmd) error {
23 g.Lock()
24 defer g.Unlock()
25 if g.cmds == nil {
26 return errors.New("shutting down")
27 }
28 err := c.Start()
29 if err != nil {
30 return err
31 }
32 g.cmds = append(g.cmds, c)
33 return nil
34 }
35
36 func (g *generatorProcesses) Remove(c *exec.Cmd) {
37 g.Lock()
38 defer g.Unlock()
39 if g.cmds == nil {
40 return
41 }
42 for n, cmd := range g.cmds {
43 if c == cmd {
44 copy(g.cmds[n:], g.cmds[n+1:])
45 g.cmds = g.cmds[:len(g.cmds)-1]
46 break
47 }
48 }
49 }
50
51 func (g *generatorProcesses) StopAll() {
52 g.Lock()
53 cmds := g.cmds
54 g.cmds = nil
55 g.Unlock()
56 for _, c := range cmds {
57 c.Process.Kill()
58 }
59 g.Wait()
60 }
61
62 var gp = &generatorProcesses{
63 cmds: make([]*exec.Cmd, 0, 2),
64 }
65
66 func (t Transfer) generate(name string, r *byteio.StickyLittleEndianReader, w *byteio.StickyLittleEndianWriter, f *os.File, size int64) error {
67 gp.Add(1)
68 defer gp.Done()
69 mp := t.c.NewMap()
70 if mp == nil {
71 return errors.New("failed to create map")
72 }
73
74 done := false
75 defer func() {
76 if !done {
77 t.c.RemoveMap(mp.ID)
78 }
79 t.c.Save()
80 }()
81
82 mp.Lock()
83 mp.Name = name
84 mapPath := mp.Path
85 mp.Server = -2
86 mp.Unlock()
87
88 t.c.Generators.mu.RLock()
89 gs := make([]data.Generator, len(t.c.Generators.List))
90 for n, g := range t.c.Generators.List {
91 gs[n] = *g
92 }
93 t.c.Generators.mu.RUnlock()
94 var g data.Generator
95 if len(gs) == 0 {
96 return errors.New("no generators installed")
97 } else if len(gs) == 1 {
98 g = gs[0]
99 } else {
100 w.WriteUint8(1)
101 w.WriteInt16(int16(len(gs)))
102 for _, tg := range gs {
103 data.WriteString(w, tg.Name)
104 }
105 if w.Err != nil {
106 return w.Err
107 }
108 gID := r.ReadInt16()
109 if r.Err != nil {
110 return r.Err
111 }
112
113 if gID < 0 || int(gID) >= len(gs) {
114 return errors.New("unknown generator")
115 }
116 g = gs[gID]
117 }
118
119 ms := DefaultMapSettings()
120 ms["level-type"] = minecraft.FlatGenerator
121 ms["generator-settings"] = "0"
122 ms["motd"] = name
123
124 j, err := os.Open(path.Join(g.Path, "data.gen"))
125 if err != nil {
126 return err
127 }
128 var gj data.GeneratorData
129 err = json.NewDecoder(j).Decode(&gj)
130 j.Close()
131 if err != nil {
132 return err
133 }
134
135 for k, v := range gj.Options {
136 ms[k] = v
137 }
138
139 pf, err := os.Create(path.Join(mapPath, "properties.map"))
140 if err != nil {
141 return err
142 }
143
144 if err = ms.WriteTo(pf); err != nil {
145 return err
146 }
147 pf.Close()
148
149 cmd := exec.Command(t.c.Settings().GeneratorExecutable)
150 cmd.ExtraFiles = append(cmd.ExtraFiles, f)
151 cmd.Dir, err = os.Getwd()
152 if err != nil {
153 return err
154 }
155 cmd.Stdout = w
156 pw, err := cmd.StdinPipe()
157 if err != nil {
158 return err
159 }
160
161 err = gp.Start(cmd)
162 if err != nil {
163 return err
164 }
165 defer gp.Remove(cmd)
166
167 pww := byteio.StickyLittleEndianWriter{Writer: pw}
168 pww.WriteUint64(t.c.Settings().GeneratorMaxMem)
169 pww.WriteInt64(size)
170 data.WriteString(&pww, g.Path)
171 data.WriteString(&pww, name)
172 data.WriteString(&pww, mapPath)
173
174 if pww.Err != nil {
175 return pww.Err
176 }
177
178 err = cmd.Wait()
179 if err != nil {
180 return err
181 }
182
183 done = true
184 mp.Lock()
185 mp.Server = -1
186 mp.Unlock()
187
188 return nil
189 }