diff options
-rw-r--r-- | README.md | 34 | ||||
-rw-r--r-- | config/app.go | 17 | ||||
-rw-r--r-- | config/config.go | 31 | ||||
-rw-r--r-- | config/config_test.go | 107 | ||||
-rw-r--r-- | config/game.go | 11 | ||||
-rw-r--r-- | config/menu.go | 23 | ||||
-rw-r--r-- | config/test_data/invalid_yaml | 1 | ||||
-rw-r--r-- | example/complete.yaml | 56 | ||||
-rw-r--r-- | go.mod | 5 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | main.go | 14 |
11 files changed, 303 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..dcafceb --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Shell Game Launcher + +Shell Game Launcher is a modern dgamelaunch alternative. It is a network based shell where anyone can sign up for an account and start playing any game hosted there. Nethack is the main target audience, but any shell game should be able to be hosted this way. + +## Content + +- [Dependencies](#dependencies) +- [Building](#building) +- [Usage](#usage) + +## Dependencies + +go is required. Only go version >= 1.15.6 on linux amd64 (Gentoo and Ubuntu 20.04) and on OpenBSD amd64 has been tested. + +## Building + +To run tests, use : +``` +go test -cover ./... +``` + +For a debug build, use : +``` +go build +``` + +For a release build, use : +``` +go build -ldflags="-s -w" +``` + +## Usage + +TODO diff --git a/config/app.go b/config/app.go new file mode 100644 index 0000000..541699c --- /dev/null +++ b/config/app.go @@ -0,0 +1,17 @@ +package config + +// App struct contains the configuration for this application +type App struct { + // WorkingDirectory is the program working directory where the user data, save files and scores are stored + WorkingDirectory string `yaml:"WorkingDirectory"` + // MaxUsers is the maximum amount of registered users to allow + MaxUsers int `yaml:"MaxUsers"` + // AllowRegistration allows registration of new users + AllowRegistration bool `yaml:"AllowRegistration"` + // MaxNickLen Maximum length for a nickname + MaxNickLen int `yaml:"MaxNickLen"` + // MenuMaxIdleTime is the maximum number of seconds a user can be idle on the menu before the program exits + MenuMaxIdleTime int `yaml:"MenuMaxIdleTime"` + // PostLoginCommands is the list of commands to execute upon login, like creating save directories for games + PostLoginCommands []string `yaml:"PostLoginCommands"` +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..2c94dbe --- /dev/null +++ b/config/config.go @@ -0,0 +1,31 @@ +package config + +import ( + "os" + + "gopkg.in/yaml.v2" +) + +type Config struct { + // AppConfig is the application level configuration entries + App App `yaml:"App"` + // Menus is the list of menus. The first one is the default menu for an anonymous user, the second one is the default menu for an authenticated user + Menus []Menu `yaml:"Menus"` + // Games is the list of games. + Games map[string]Game `yaml:"Games"` +} + +// LoadFile loads the config from a given file +func LoadFile(path string) (config Config, err error) { + var f *os.File + f, err = os.Open(path) + if err != nil { + return + } + defer f.Close() + decoder := yaml.NewDecoder(f) + if err = decoder.Decode(&config); err != nil { + return + } + return +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 0000000..adbfc7e --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,107 @@ +package config + +import ( + "reflect" + "testing" +) + +func TestLoadFile(t *testing.T) { + _, err := LoadFile("test_data/non-existant") + if err == nil { + t.Fatal("non-existant config file failed without error") + } + _, err = LoadFile("test_data/invalid_yaml") + if err == nil { + t.Fatal("invalid_yaml config file failed without error") + } + config, err := LoadFile("../example/complete.yaml") + want := Config{ + App: App{ + WorkingDirectory: "var/", + MaxUsers: 512, + AllowRegistration: true, + MaxNickLen: 15, + MenuMaxIdleTime: 600, + PostLoginCommands: []string{ + "mkdir %w/userdata/%u", + "mkdir %w/userdata/%u/dumplog", + "mkdir %w/userdata/%u/ttyrec", + }, + }, + Menus: []Menu{ + Menu{ + Banner: "Shell Game Launcher - Anonymous access%n======================================", + XOffset: 5, + YOffset: 2, + MenuEntries: []MenuEntry{ + MenuEntry{ + Key: "l", + Label: "login", + Action: "login", + }, + MenuEntry{ + Key: "r", + Label: "register", + Action: "register", + }, + MenuEntry{ + Key: "w", + Label: "watch", + Action: "watch_menu", + }, + MenuEntry{ + Key: "s", + Label: "scores", + Action: "scores", + }, + MenuEntry{ + Key: "q", + Label: "quit", + Action: "quit", + }, + }, + }, + Menu{ + Banner: "Shell Game Launcher%n===================", + XOffset: 5, + YOffset: 2, + MenuEntries: []MenuEntry{ + MenuEntry{ + Key: "p", + Label: "play Nethack 3.7", + Action: "play nethack3.7", + }, + MenuEntry{ + Key: "o", + Label: "edit game options", + Action: "options", + }, + MenuEntry{ + Key: "w", + Label: "watch", + Action: "watch_menu", + }, + MenuEntry{ + Key: "s", + Label: "scores", + Action: "scores", + }, + MenuEntry{ + Key: "q", + Label: "quit", + Action: "quit", + }, + }, + }, + }, + Games: map[string]Game{ + "nethack3.7": Game{ + ChrootPath: "/opt/nethack", + FileMode: "0666", + }, + }, + } + if err != nil || !reflect.DeepEqual(want, config) { + t.Fatalf("complete example failed:\nerror %v\nwant:%+v\ngot: %+v", err, want, config) + } +} diff --git a/config/game.go b/config/game.go new file mode 100644 index 0000000..0eea917 --- /dev/null +++ b/config/game.go @@ -0,0 +1,11 @@ +package config + +// Game struct containers the configuration for a game +type Game struct { + // ChrootPath is the chroot path for the game + ChrootPath string `yaml:"ChrootPath"` + // FileMode is the file mode to use when copying files + FileMode string `yaml:"FileMode"` + // Commands is the command list + Commands []string `yaml:"Commands"` +} diff --git a/config/menu.go b/config/menu.go new file mode 100644 index 0000000..2913be2 --- /dev/null +++ b/config/menu.go @@ -0,0 +1,23 @@ +package config + +// Menu struct describes a screen menu +type Menu struct { + // Banner is the banner to display before the menu + Banner string `yaml:"Banner"` + // XOffset is the X offset between the banner and the menu + XOffset int `yaml:"XOffset"` + // YOffset is the Y offset between the banner and the menu + YOffset int `yaml:"YOffset"` + // Commands is the list of commands in the menu + MenuEntries []MenuEntry `yaml:"MenuEntries"` +} + +// MenuEntry struct describes a menu entry +type MenuEntry struct { + // Key is the key associated with the action. We need to store it as a string because of how yaml unmarshal works + Key string `yaml:"Key"` + // Label is the text displayed on the menu + Label string `yaml:"Label"` + // Action is the action executed when the menu entry is selected + Action string `yaml:"Action"` +} diff --git a/config/test_data/invalid_yaml b/config/test_data/invalid_yaml new file mode 100644 index 0000000..db1ddad --- /dev/null +++ b/config/test_data/invalid_yaml @@ -0,0 +1 @@ +blargh(ads) diff --git a/example/complete.yaml b/example/complete.yaml new file mode 100644 index 0000000..31cea74 --- /dev/null +++ b/example/complete.yaml @@ -0,0 +1,56 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + - Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: s + Label: scores + Action: scores + - Key: q + Label: quit + Action: quit + - Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + - Key: o + Label: edit game options + Action: options + - Key: w + Label: watch + Action: watch_menu + - Key: s + Label: scores + Action: scores + - Key: q + Label: quit + Action: quit + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + Commands: @@ -0,0 +1,5 @@ +module shell-game-launcher + +go 1.15 + +require gopkg.in/yaml.v2 v2.4.0 @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -0,0 +1,14 @@ +package main + +import ( + "log" + "shell-game-launcher/config" +) + +func main() { + config, err := config.LoadFile("example/complete.yaml") + if err != nil { + log.Fatal(err) + } + log.Printf("%+v", config) +} |