Skip to content

Commit b4bb862

Browse files
committed
Generate event definitions
1 parent 6ba427e commit b4bb862

File tree

8 files changed

+417
-1
lines changed

8 files changed

+417
-1
lines changed

commands/new.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package commands
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/code-game-project/codegame-cli/external"
14+
"github.com/code-game-project/codegame-cli/input"
15+
"github.com/ogier/pflag"
16+
)
17+
18+
func New() error {
19+
var project string
20+
if pflag.NArg() >= 2 {
21+
project = strings.ToLower(pflag.Arg(1))
22+
} else {
23+
var err error
24+
project, err = input.Select("Which type of project would you like to create?", []string{"Game Server", "Game Client"}, []string{"server", "client"})
25+
if err != nil {
26+
return err
27+
}
28+
}
29+
30+
switch project {
31+
case "server":
32+
return newServer()
33+
case "client":
34+
return newClient()
35+
default:
36+
return fmt.Errorf("Unknown project type: %s", project)
37+
}
38+
}
39+
40+
func newServer() error {
41+
return errors.New("Not implemented.")
42+
}
43+
44+
func newClient() error {
45+
domain, err := input.Input("Enter the domain of the game server:")
46+
if err != nil {
47+
return err
48+
}
49+
if strings.HasPrefix(domain, "http://") {
50+
domain = strings.TrimPrefix(domain, "http://")
51+
} else if strings.HasPrefix(domain, "https://") {
52+
domain = strings.TrimPrefix(domain, "https://")
53+
} else if strings.HasPrefix(domain, "ws://") {
54+
domain = strings.TrimPrefix(domain, "ws://")
55+
} else if strings.HasPrefix(domain, "wss://") {
56+
domain = strings.TrimPrefix(domain, "wss://")
57+
}
58+
domain = strings.TrimSuffix(domain, "/")
59+
ssl := isSSL(domain)
60+
name, _, err := getCodeGameInfo(baseURL(domain, ssl))
61+
if err != nil {
62+
return err
63+
}
64+
cgeVersion, err := getCGEVersion(baseURL(domain, ssl))
65+
if err != nil {
66+
return err
67+
}
68+
69+
projectName, err := input.Input("Project name:")
70+
if err != nil {
71+
return err
72+
}
73+
74+
err = os.MkdirAll(projectName, 0755)
75+
if err != nil {
76+
return err
77+
}
78+
79+
var language string
80+
if pflag.NArg() >= 3 {
81+
language = strings.ToLower(pflag.Arg(2))
82+
} else {
83+
var err error
84+
language, err = input.Select("In which language do you want to write your project?", []string{"Go"}, []string{"go"})
85+
if err != nil {
86+
return err
87+
}
88+
}
89+
90+
eventsOutput := projectName
91+
if language == "go" {
92+
eventsOutput = filepath.Join(projectName, name)
93+
}
94+
95+
err = external.CGGenEvents(eventsOutput, baseURL(domain, ssl), cgeVersion, language)
96+
if err != nil {
97+
return err
98+
}
99+
return nil
100+
}
101+
102+
func getCodeGameInfo(baseURL string) (string, string, error) {
103+
type response struct {
104+
Name string `json:"name"`
105+
CGVersion string `json:"cg_version"`
106+
}
107+
res, err := http.Get(baseURL + "/info")
108+
if err != nil {
109+
return "", "", err
110+
}
111+
defer res.Body.Close()
112+
113+
var data response
114+
err = json.NewDecoder(res.Body).Decode(&data)
115+
if err != nil {
116+
return "", "", err
117+
}
118+
119+
return data.Name, data.CGVersion, nil
120+
}
121+
122+
func getCGEVersion(baseURL string) (string, error) {
123+
res, err := http.Get(baseURL + "/events")
124+
if err != nil {
125+
return "", err
126+
}
127+
defer res.Body.Close()
128+
129+
data, err := io.ReadAll(res.Body)
130+
if err != nil {
131+
return "", err
132+
}
133+
return parseCGEVersion([]rune(string(data))), nil
134+
}
135+
136+
func parseCGEVersion(runes []rune) string {
137+
index := 0
138+
commentNestingLevel := 0
139+
for index < len(runes) && (runes[index] == ' ' || runes[index] == '\r' || runes[index] == '\n' || runes[index] == '\t' || (index < len(runes)-1 && runes[index] == '/' && runes[index+1] == '*') || (index < len(runes)-1 && runes[index] == '*' && runes[index+1] == '/') || (index < len(runes)-1 && runes[index] == '/' && runes[index+1] == '/') || commentNestingLevel > 0) {
140+
if runes[index] == '/' {
141+
if runes[index+1] == '/' {
142+
for index < len(runes) && runes[index] != '\n' {
143+
index++
144+
}
145+
} else {
146+
commentNestingLevel++
147+
}
148+
}
149+
if runes[index] == '*' {
150+
commentNestingLevel--
151+
}
152+
index++
153+
}
154+
155+
words := strings.Fields(string(runes[index:]))
156+
for i, w := range words {
157+
if w == "version" && i < len(words)-1 {
158+
return words[i+1]
159+
}
160+
}
161+
162+
return ""
163+
}
164+
165+
func baseURL(domain string, ssl bool) string {
166+
if ssl {
167+
return "https://" + domain
168+
} else {
169+
return "http://" + domain
170+
}
171+
}
172+
173+
func isSSL(domain string) bool {
174+
res, err := http.Get("https://" + domain)
175+
if err == nil {
176+
res.Body.Close()
177+
return true
178+
}
179+
return false
180+
}

external/archive.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package external
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"io"
7+
"os"
8+
)
9+
10+
func UntargzFile(source io.Reader, fileName, outputFileName string) error {
11+
archive, err := gzip.NewReader(source)
12+
if err != nil {
13+
return err
14+
}
15+
defer archive.Close()
16+
17+
tarReader := tar.NewReader(archive)
18+
19+
for {
20+
header, err := tarReader.Next()
21+
if err == io.EOF {
22+
break
23+
} else if err != nil {
24+
return err
25+
}
26+
27+
info := header.FileInfo()
28+
if info.Name() == fileName {
29+
file, err := os.OpenFile(outputFileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
30+
if err != nil {
31+
return err
32+
}
33+
defer file.Close()
34+
_, err = io.Copy(file, tarReader)
35+
if err != nil {
36+
return err
37+
}
38+
return nil
39+
}
40+
}
41+
return nil
42+
}

external/cg_gen_events.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package external
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"os"
7+
"path/filepath"
8+
"runtime"
9+
"strings"
10+
11+
"github.com/adrg/xdg"
12+
)
13+
14+
var cgGenEventsPath = filepath.Join(xdg.DataHome, "codegame", "bin", "cg-gen-events")
15+
16+
func InstallCGGenEvents(cgeVersion string) error {
17+
exeName := fmt.Sprintf("cg-gen-events-%s", strings.ReplaceAll(cgeVersion, ".", "_"))
18+
if runtime.GOOS == "windows" {
19+
exeName = exeName + ".exe"
20+
}
21+
22+
if _, err := os.Stat(filepath.Join(cgGenEventsPath, exeName)); err == nil {
23+
return nil
24+
}
25+
26+
version, err := GithubTagFromMinorVersion("code-game-project", "cg-gen-events", cgeVersion)
27+
if err != nil {
28+
return err
29+
}
30+
31+
filename := fmt.Sprintf("cg-gen-events-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH)
32+
if runtime.GOOS == "windows" {
33+
filename = fmt.Sprintf("cg-gen-events-%s-%s.zip", runtime.GOOS, runtime.GOARCH)
34+
}
35+
36+
res, err := http.Get(fmt.Sprintf("https://github.com/code-game-project/cg-gen-events/releases/download/%s/%s", version, filename))
37+
if err != nil {
38+
return err
39+
}
40+
defer res.Body.Close()
41+
42+
err = os.MkdirAll(cgGenEventsPath, 0755)
43+
if err != nil {
44+
return err
45+
}
46+
47+
return UntargzFile(res.Body, "cg-gen-events", filepath.Join(cgGenEventsPath, exeName))
48+
}
49+
50+
func CGGenEvents(outputDir, url, cgeVersion, language string) error {
51+
exeName := fmt.Sprintf("cg-gen-events-%s", strings.ReplaceAll(cgeVersion, ".", "_"))
52+
if runtime.GOOS == "windows" {
53+
exeName = exeName + ".exe"
54+
}
55+
56+
if _, err := os.Stat(filepath.Join(cgGenEventsPath, exeName)); err != nil {
57+
err = InstallCGGenEvents(cgeVersion)
58+
if err != nil {
59+
return err
60+
}
61+
}
62+
63+
return Execute(filepath.Join(cgGenEventsPath, exeName), url, "--languages", language, "--output", outputDir)
64+
}

external/external.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package external
22

33
import (
4+
"encoding/json"
5+
"errors"
46
"fmt"
7+
"net/http"
58
"os"
69
"os/exec"
710
"os/user"
811
"runtime"
912
"strings"
1013
)
1114

15+
var ErrTagNotFound = errors.New("tag not found")
16+
1217
func IsInstalled(programName string) bool {
1318
_, err := exec.LookPath(programName)
1419
return err == nil
@@ -58,3 +63,26 @@ func OpenBrowser(url string) error {
5863
return fmt.Errorf("unsupported platform")
5964
}
6065
}
66+
67+
func GithubTagFromMinorVersion(owner, repo, version string) (string, error) {
68+
res, err := http.Get(fmt.Sprintf("https://api.github.com/repos/%s/%s/tags", owner, repo))
69+
if err != nil {
70+
return "", err
71+
}
72+
defer res.Body.Close()
73+
type response []struct {
74+
Name string `json:"name"`
75+
}
76+
var data response
77+
err = json.NewDecoder(res.Body).Decode(&data)
78+
if err != nil {
79+
return "", err
80+
}
81+
82+
for _, tag := range data {
83+
if strings.HasPrefix(tag.Name, "v"+version) {
84+
return tag.Name, nil
85+
}
86+
}
87+
return "", ErrTagNotFound
88+
}

go.mod

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,18 @@ module github.com/code-game-project/codegame-cli
22

33
go 1.18
44

5-
require github.com/ogier/pflag v0.0.1
5+
require (
6+
github.com/AlecAivazis/survey/v2 v2.3.4
7+
github.com/adrg/xdg v0.4.0
8+
github.com/ogier/pflag v0.0.1
9+
)
10+
11+
require (
12+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
13+
github.com/mattn/go-colorable v0.1.2 // indirect
14+
github.com/mattn/go-isatty v0.0.8 // indirect
15+
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
16+
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
17+
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
18+
golang.org/x/text v0.3.3 // indirect
19+
)

go.sum

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,41 @@
1+
github.com/AlecAivazis/survey/v2 v2.3.4 h1:pchTU9rsLUSvWEl2Aq9Pv3k0IE2fkqtGxazskAMd9Ng=
2+
github.com/AlecAivazis/survey/v2 v2.3.4/go.mod h1:hrV6Y/kQCLhIZXGcriDCUBtB3wnN7156gMXJ3+b23xM=
3+
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
4+
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
5+
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
6+
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
7+
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
8+
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
9+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
11+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12+
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
13+
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
14+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
15+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
16+
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
17+
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
18+
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
19+
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
20+
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
21+
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
122
github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750=
223
github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g=
24+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
25+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
26+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
27+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
28+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
29+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
30+
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
31+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
32+
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
33+
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
34+
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
35+
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
36+
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
37+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
38+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
39+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
40+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
41+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)