Skip to content

Commit 52bb44c

Browse files
committed
Implement Go client template
1 parent b4bb862 commit 52bb44c

File tree

6 files changed

+220
-20
lines changed

6 files changed

+220
-20
lines changed

commands/new.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,26 @@ func newServer() error {
4242
}
4343

4444
func newClient() error {
45-
domain, err := input.Input("Enter the domain of the game server:")
45+
url, err := input.Input("Enter the URL of the game server:")
4646
if err != nil {
4747
return err
4848
}
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))
49+
if strings.HasPrefix(url, "http://") {
50+
url = strings.TrimPrefix(url, "http://")
51+
} else if strings.HasPrefix(url, "https://") {
52+
url = strings.TrimPrefix(url, "https://")
53+
} else if strings.HasPrefix(url, "ws://") {
54+
url = strings.TrimPrefix(url, "ws://")
55+
} else if strings.HasPrefix(url, "wss://") {
56+
url = strings.TrimPrefix(url, "wss://")
57+
}
58+
url = strings.TrimSuffix(url, "/")
59+
ssl := isSSL(url)
60+
name, cgVersion, err := getCodeGameInfo(baseURL(url, ssl))
6161
if err != nil {
6262
return err
6363
}
64-
cgeVersion, err := getCGEVersion(baseURL(domain, ssl))
64+
cgeVersion, err := getCGEVersion(baseURL(url, ssl))
6565
if err != nil {
6666
return err
6767
}
@@ -89,13 +89,26 @@ func newClient() error {
8989

9090
eventsOutput := projectName
9191
if language == "go" {
92-
eventsOutput = filepath.Join(projectName, name)
92+
eventsOutput = filepath.Join(projectName, strings.ReplaceAll(strings.ReplaceAll(name, "-", ""), "_", ""))
9393
}
9494

95-
err = external.CGGenEvents(eventsOutput, baseURL(domain, ssl), cgeVersion, language)
95+
err = external.CGGenEvents(eventsOutput, baseURL(url, ssl), cgeVersion, language)
96+
if err != nil {
97+
fmt.Fprintln(os.Stderr, "\x1b[31mFailed to generate event definitions:", err, "\x1b[0m")
98+
}
99+
100+
switch language {
101+
case "go":
102+
err = newClientGo(projectName, url, cgVersion)
103+
default:
104+
return fmt.Errorf("Unsupported language: %s", language)
105+
}
96106
if err != nil {
97107
return err
98108
}
109+
110+
fmt.Printf("\x1b[32mSuccessfully created project in '%s'.\n\x1b[0m", projectName)
111+
99112
return nil
100113
}
101114

commands/new_go.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
9+
"github.com/code-game-project/codegame-cli/external"
10+
"github.com/code-game-project/codegame-cli/input"
11+
"github.com/code-game-project/codegame-cli/templates"
12+
)
13+
14+
func newClientGo(projectName, serverURL, cgVersion string) error {
15+
err := createGoTemplate(projectName, serverURL)
16+
if err != nil {
17+
return err
18+
}
19+
20+
err = installGoLibrary(projectName, cgVersion)
21+
if err != nil {
22+
return err
23+
}
24+
25+
err = external.ExecuteInDir(projectName, "go", "mod", "tidy")
26+
if err != nil {
27+
return err
28+
}
29+
30+
return external.ExecuteInDir(projectName, "goimports", "-w", "main.go")
31+
}
32+
33+
func createGoTemplate(projectName, serverURL string) error {
34+
module, err := input.Input("Project module path:")
35+
if err != nil {
36+
return err
37+
}
38+
39+
out, err := external.ExecuteInDirHidden(projectName, "go", "mod", "init", module)
40+
if err != nil {
41+
fmt.Println(out)
42+
return err
43+
}
44+
45+
return os.WriteFile(filepath.Join(projectName, "main.go"), []byte(strings.ReplaceAll(fmt.Sprintf(templates.Go, serverURL), "$", "%")), 0644)
46+
}
47+
48+
func installGoLibrary(projectName, cgVersion string) error {
49+
clientVersion := external.ClientVersionFromCGVersion("code-game-project", "go-client", cgVersion)
50+
51+
if clientVersion == "latest" {
52+
out, err := external.ExecuteInDirHidden(projectName, "go", "get")
53+
if err != nil {
54+
fmt.Println(out)
55+
}
56+
return err
57+
}
58+
59+
majorVersion := strings.Split(clientVersion, ".")[0]
60+
tag, err := external.GithubTagFromVersion("code-game-project", "go-client", clientVersion)
61+
if err != nil {
62+
return err
63+
}
64+
path := "github.com/code-game-project/go-client/cg"
65+
if majorVersion != "0" && majorVersion != "1" {
66+
path = fmt.Sprintf("github.com/code-game-project/go-client/v%s/cg", majorVersion)
67+
}
68+
path += "@" + tag
69+
70+
out, err := external.ExecuteInDirHidden(projectName, "go", "get", path)
71+
if err != nil {
72+
fmt.Println(out)
73+
}
74+
return err
75+
}

external/cg_gen_events.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func InstallCGGenEvents(cgeVersion string) error {
2323
return nil
2424
}
2525

26-
version, err := GithubTagFromMinorVersion("code-game-project", "cg-gen-events", cgeVersion)
26+
version, err := GithubTagFromVersion("code-game-project", "cg-gen-events", cgeVersion)
2727
if err != nil {
2828
return err
2929
}

external/external.go

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"math"
78
"net/http"
89
"os"
910
"os/exec"
1011
"os/user"
1112
"runtime"
13+
"strconv"
1214
"strings"
1315
)
1416

@@ -27,11 +29,24 @@ func Execute(programName string, args ...string) error {
2729
return cmd.Run()
2830
}
2931

30-
func ExecuteHidden(programName string, args ...string) (string, error) {
32+
func ExecuteInDir(workingDir, programName string, args ...string) error {
3133
cmd := exec.Command(programName, args...)
34+
cmd.Dir = workingDir
3235
cmd.Stdin = os.Stdin
3336
cmd.Stdout = os.Stdout
3437
cmd.Stderr = os.Stderr
38+
return cmd.Run()
39+
}
40+
41+
func ExecuteHidden(programName string, args ...string) (string, error) {
42+
cmd := exec.Command(programName, args...)
43+
out, err := cmd.CombinedOutput()
44+
return string(out), err
45+
}
46+
47+
func ExecuteInDirHidden(workingDir, programName string, args ...string) (string, error) {
48+
cmd := exec.Command(programName, args...)
49+
cmd.Dir = workingDir
3550
out, err := cmd.CombinedOutput()
3651
return string(out), err
3752
}
@@ -64,7 +79,7 @@ func OpenBrowser(url string) error {
6479
}
6580
}
6681

67-
func GithubTagFromMinorVersion(owner, repo, version string) (string, error) {
82+
func GithubTagFromVersion(owner, repo, version string) (string, error) {
6883
res, err := http.Get(fmt.Sprintf("https://api.github.com/repos/%s/%s/tags", owner, repo))
6984
if err != nil {
7085
return "", err
@@ -86,3 +101,90 @@ func GithubTagFromMinorVersion(owner, repo, version string) (string, error) {
86101
}
87102
return "", ErrTagNotFound
88103
}
104+
105+
func ClientVersionFromCGVersion(owner, repo, cgVersion string) string {
106+
res, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/main/versions.json", owner, repo))
107+
if err != nil {
108+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: Couldn't fetch versions.json. Using latest client library version.\n\x1b[0m")
109+
return "latest"
110+
}
111+
defer res.Body.Close()
112+
113+
var versions map[string]string
114+
err = json.NewDecoder(res.Body).Decode(&versions)
115+
if err != nil {
116+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: Invalid versions.json. Using latest client library version.\n\x1b[0m")
117+
return "latest"
118+
}
119+
120+
// check exact match
121+
if v, ok := versions[cgVersion]; ok {
122+
return v
123+
}
124+
125+
parts := strings.Split(cgVersion, ".")
126+
if len(parts) < 2 {
127+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: Invalid versions.json. Using latest client library version.\n\x1b[0m")
128+
return "latest"
129+
}
130+
major := parts[0]
131+
132+
// get all minor versions of the requested major version
133+
compatibleMinorVersions := make([]int, 0)
134+
for v := range versions {
135+
clientParts := strings.Split(v, ".")
136+
if len(clientParts) < 2 {
137+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: Invalid versions.json. Using latest client library version.\n\x1b[0m")
138+
return "latest"
139+
}
140+
clientMajor := clientParts[0]
141+
if major == clientMajor {
142+
minor, err := strconv.Atoi(clientParts[1])
143+
if err != nil {
144+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: Invalid versions.json. Using latest client library version.\n\x1b[0m")
145+
return "latest"
146+
}
147+
compatibleMinorVersions = append(compatibleMinorVersions, minor)
148+
}
149+
}
150+
if len(compatibleMinorVersions) == 0 {
151+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: No compatible client library version found. Using latest client library version.\n\x1b[0m")
152+
return "latest"
153+
}
154+
155+
minorStr := parts[1]
156+
minor, err := strconv.Atoi(minorStr)
157+
if err != nil {
158+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: Invalid versions.json. Using latest client library version.\n\x1b[0m")
159+
return "latest"
160+
}
161+
162+
// check closest minor version above requested
163+
closestMinor := -1
164+
for _, v := range compatibleMinorVersions {
165+
if v > minor && float64(closestMinor-minor) > float64(v-minor) {
166+
closestMinor = v
167+
}
168+
}
169+
if closestMinor >= 0 {
170+
v := fmt.Sprintf("%s.%d", major, minor)
171+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: No exact version match found. Using client library version %s.\n\x1b[0m", v)
172+
return v
173+
}
174+
175+
// check closest minor version below requested
176+
closestMinor = math.MaxInt
177+
for _, v := range compatibleMinorVersions {
178+
if v < minor && float64(minor-closestMinor) > float64(minor-v) {
179+
closestMinor = v
180+
}
181+
}
182+
if closestMinor >= 0 {
183+
v := fmt.Sprintf("%s.%d", major, minor)
184+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: No exact version match found. Using client library version %s.\n\x1b[0m", v)
185+
return v
186+
}
187+
188+
fmt.Fprintf(os.Stderr, "\x1b[33mWARNING: No compatible client library version found. Using latest client library version.\n\x1b[0m")
189+
return "latest"
190+
}

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func main() {
4444
os.Exit(1)
4545
}
4646
if err != nil {
47-
fmt.Fprintln(os.Stderr, err)
47+
fmt.Fprint(os.Stderr, "\x1b[31m", err, "\n\x1b[0m")
4848
os.Exit(1)
4949
}
5050
}

templates/go.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package templates
2+
3+
const Go = `package main
4+
5+
func main() {
6+
socket, err := cg.NewSocket("%s")
7+
if err != nil {
8+
log.Fatalf("failed to connect to server: $s", err)
9+
}
10+
}`

0 commit comments

Comments
 (0)