From 484734ab36b06a9e6d35348e357312d99522302c Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 23 Dec 2020 12:01:05 +0100 Subject: Implemented the configuration file format --- README.md | 34 ++++++++++++++ config/app.go | 17 +++++++ config/config.go | 31 ++++++++++++ config/config_test.go | 107 ++++++++++++++++++++++++++++++++++++++++++ config/game.go | 11 +++++ config/menu.go | 23 +++++++++ config/test_data/invalid_yaml | 1 + example/complete.yaml | 56 ++++++++++++++++++++++ go.mod | 5 ++ go.sum | 4 ++ main.go | 14 ++++++ 11 files changed, 303 insertions(+) create mode 100644 README.md create mode 100644 config/app.go create mode 100644 config/config.go create mode 100644 config/config_test.go create mode 100644 config/game.go create mode 100644 config/menu.go create mode 100644 config/test_data/invalid_yaml create mode 100644 example/complete.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go 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: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4237bdf --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module shell-game-launcher + +go 1.15 + +require gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dd0bc19 --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5ab16fd --- /dev/null +++ b/main.go @@ -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) +} -- cgit v1.2.3