feat: Add viper config and basic arguments

This commit is contained in:
Wanjohi Ryan
2024-07-08 04:06:28 +03:00
parent a0303bb45f
commit 08597cef4b
3 changed files with 98 additions and 231 deletions

11
.nestri.yml Normal file
View File

@@ -0,0 +1,11 @@
version: "0.1"
games:
CyberPunk2047:
directory: /path/to/game
executable: CyberPunk2047.exe
gpu: 1
vendor: GPUidhere #(ex: vendor:N) e.g. nvidia:0 or amd:1
resolution:
height: 1080
width: 1920

View File

@@ -5,14 +5,18 @@ package cmd
import (
_ "embed"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
//go:embed nestri.ascii
var art string
var cfgFile string
type GameConfig struct {
Directory string
Executable string
@@ -44,12 +48,49 @@ func Execute() {
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)")
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.nestri.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Search for config in the current directory
viper.AddConfigPath(".")
viper.SetConfigName(".nestri")
viper.SetConfigType("yaml")
// If not found in current directory, check in $HOME/.nestri/
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Find home directory.
home, err := os.UserHomeDir()
cobra.CheckErr(err)
// Search config in home directory with name ".nestri" (without extension).
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".nestri")
}
}
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err != nil {
fmt.Fprintln(os.Stderr, "Could not find a config file in local directory or in $HOME directory:")
}
}

View File

@@ -5,13 +5,14 @@ import (
"runtime"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// this is the "nestri run" subcommand, takes no arguments for now
var runCmd = &cobra.Command{
Use: "run [options] [game]",
Use: "run",
Short: "Run a game using nestri",
Args: cobra.MaximumNArgs(1),
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if runtime.GOOS != "linux" {
//make sure os is linux
@@ -31,230 +32,33 @@ var runCmd = &cobra.Command{
game := args[len(args)-1]
// Load game configuration
// var gameConfig GameConfig
// if err := viper.UnmarshalKey(fmt.Sprintf("games.%s", game), &gameConfig); err != nil {
// return fmt.Errorf("error parsing game configuration: %w", err)
// }
var gameConfig GameConfig
if err := viper.UnmarshalKey(fmt.Sprintf("games.%s", game), &gameConfig); err != nil {
return fmt.Errorf("error parsing game configuration: %w", err)
}
// // Override config with command-line flags
// cmd.Flags().Visit(func(f *pflag.Flag) {
// switch f.Name {
// case "directory":
// gameConfig.Directory = viper.GetString(f.Name)
// case "executable":
// gameConfig.Executable = viper.GetString(f.Name)
// case "gpu":
// gameConfig.GPU = viper.GetInt(f.Name)
// case "vendor":
// gameConfig.Vendor = viper.GetString(f.Name)
// case "resolution-height":
// gameConfig.Resolution.Height = viper.GetInt(f.Name)
// case "resolution-width":
// gameConfig.Resolution.Width = viper.GetInt(f.Name)
// }
// })
flags := cmd.Flags()
fmt.Printf("Running game: %s\n\n", game)
if flags.Changed("directory") || flags.Changed("d") {
gameConfig.Directory, _ = flags.GetString("directory")
}
if flags.Changed("executable") || flags.Changed("x") {
gameConfig.Executable, _ = flags.GetString("executable")
}
if flags.Changed("gpu") {
gameConfig.GPU, _ = flags.GetInt("gpu")
}
if flags.Changed("vendor") || flags.Changed("v") {
gameConfig.Vendor, _ = flags.GetString("vendor")
}
if flags.Changed("height") || flags.Changed("H") {
gameConfig.Resolution.Height, _ = flags.GetInt("height")
}
if flags.Changed("width") || flags.Changed("W") {
gameConfig.Resolution.Width, _ = flags.GetInt("width")
}
// cli, err := client.NewClientWithOpts(client.FromEnv)
// if err != nil {
// return fmt.Errorf("error creating Docker client: %w", err)
// }
// ctx := context.Background()
// var game string
// if len(args) > 0 {
// game = args[0]
// viper.Set("game", game)
// viper.WriteConfig()
// } else {
// game = viper.GetString("game")
// if filepath.Ext(game) != ".exe" {
// return fmt.Errorf("Make sure the game is a .exe")
// }
// if game == "" {
// return fmt.Errorf("no game specified and no previous game selected")
// }
// }
// fmt.Printf("Running game: %s\n\n", game)
// cli, err := client.NewClientWithOpts()
// if err != nil {
// panic(err)
// }
// ctx := context.Background()
// resp, err := cli.ContainerCreate(ctx, &container.Config{
// Image: "hello-world",
// }, nil, nil, nil, "hello-world")
// if err != nil {
// panic(err)
// }
// if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
// panic(err)
// }
// // Attach to the container to get logs
// out, err := cli.ContainerLogs(ctx, resp.ID, container.LogsOptions{ShowStdout: true, ShowStderr: true, Follow: true})
// if err != nil {
// fmt.Printf("Error attaching to container logs: %s\n", err)
// }
// defer out.Close()
// // Copy the logs to stdout and stderr
// stdcopy.StdCopy(os.Stdout, os.Stderr, out)
// // Wait for the container to finish
// statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
// select {
// case err := <-errCh:
// if err != nil {
// fmt.Printf("Error waiting for container: %s\n", err)
// }
// case <-statusCh:
// fmt.Println("Container finished")
// }
// // Clean up the container
// if err := cli.ContainerRemove(ctx, resp.ID, container.RemoveOptions{}); err != nil {
// fmt.Printf("Error removing container: %s\n", err)
// }
// if gpu > 0 {
// fmt.Print("Using gpu %s\n", gpu)
// }
// if hdr {
// fmt.Println("Enabling HDR mode")
// }
//get linux version
// versionCmd := exec.Command("grep", "VERSION", "/etc/os-release")
// versionOutput, err := versionCmd.CombinedOutput()
// if err != nil {
// return fmt.Errorf("error getting linux version:")
// }
// fmt.Printf("Linux version:\n%s\n", string(versionOutput))
// //Step 1: change to games dir
// fmt.Println("changing to game dir.") //this is a temp command for debug as well as leads to a hardcoded dir
// HomeDir, err := os.UserHomeDir()
// if err != nil {
// return fmt.Errorf("error getting home directory %v\n", err)
// }
// err = os.Chdir(fmt.Sprintf("%s/game", HomeDir))
// if err != nil {
// return fmt.Errorf("error changing directory: %v\n", err)
// }
// //verify we are in game dir
// dir, err := os.Getwd()
// if err != nil {
// return fmt.Errorf("error getting current directory: %v\n", err)
// }
// fmt.Printf("Current directory: %s\n\n", dir)
// //list games dir
// listDir := exec.Command("ls", "-la", ".")
// listDirOutput, err := listDir.CombinedOutput()
// if err != nil {
// fmt.Errorf("error listing games: %v\n")
// }
// fmt.Printf("List of Games: \n%s\n", listDirOutput)
// //step 2: Generate a Session ID
// //generate id
// SID := exec.Command("bash", "-c", "head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16")
// //save output to variable
// output, err := SID.Output()
// if err != nil {
// fmt.Errorf("Error generating Session ID: %v\n", err)
// }
// sessionID := strings.TrimSpace(string(output))
// fmt.Printf("Your Session ID is: %s\n\n", sessionID)
// //step 3: Launch netris server
// fmt.Println("Installing Netris/Launching Netris Server")
// checkRunning := exec.Command("sudo", "docker", "ps", "-q", "-f", "name=netris")
// containerId, err := checkRunning.Output()
// if err != nil {
// return fmt.Errorf("error checking running Docker container: %v", err)
// }
// if len(containerId) == 0 {
// checkExisting := exec.Command("sudo", "docker", "ps", "-aq", "-f", "name=netris")
// containerId, err = checkExisting.Output()
// if err != nil {
// return fmt.Errorf("error checking for existing docker container: %v", err)
// }
// if len(containerId) == 0 {
// installCmd := exec.Command(
// "sudo", "docker", "run", "-d", "--gpus", "all", "--device=/dev/dri",
// "--name", "netris", "-it", "--entrypoint", "/bin/bash",
// "-e", fmt.Sprintf("SESSION_ID=%s", sessionID),
// "-v", fmt.Sprintf("%s:/game", dir), "-p", "8080:8080/udp",
// "--cap-add=SYS_NICE", "--cap-add=SYS_ADMIN", "ghcr.io/netrisdotme/netris/server:nightly",
// )
// installCmd.Stdout = os.Stdout
// installCmd.Stderr = os.Stderr
// if err := installCmd.Run(); err != nil {
// return fmt.Errorf("error running docker command: %v", err)
// }
// } else {
// startContainer := exec.Command("sudo", "docker", "start", "netris")
// startContainer.Stdout = os.Stdout
// startContainer.Stderr = os.Stderr
// if err := startContainer.Run(); err != nil {
// return fmt.Errorf("error starting existing Docker container: %v", err)
// }
// }
// }
// //main part of step 4:
// //start netris server
// fmt.Println("starting netris server\n\n")
// checkFileCmd := exec.Command("sudo", "docker", "exec", "netris", "ls", "-la", "/tmp")
// output, err = checkFileCmd.Output()
// if err != nil {
// return fmt.Errorf("error checking /tmp dir in docker container: %v\n", err)
// }
// if !strings.Contains(string(output), ".X11-unix") {
// startupCmd := exec.Command("sudo", "docker", "exec", "netris", "/etc/startup.sh", ">", "/dev/null", "&")
// startupCmd.Stdout = os.Stdout
// startupCmd.Stderr = os.Stderr
// if err := startupCmd.Run(); err != nil {
// return fmt.Errorf("error running startup command: %v\n", err)
// }
// for {
// time.Sleep(7 * time.Minute)
// output, err := checkFileCmd.Output()
// if err != nil {
// return fmt.Errorf("error checking /tmp directory in container: %v\n", err)
// }
// if strings.Contains(string(output), ".X11-unix") {
// break
// }
// }
// }
// gameCmd := fmt.Sprintf("netris-proton -pr %s", game)
// execCmd := exec.Command("sudo", "docker", "exec", "netris", gameCmd)
// execCmd.Stdout = os.Stdout
// execCmd.Stderr = os.Stderr
// if err := execCmd.Run(); err != nil {
// return fmt.Errorf("error executing game command in docker container: %v\n", err)
// }
fmt.Println("Game config:", gameConfig)
return nil
},
@@ -263,13 +67,24 @@ var runCmd = &cobra.Command{
func init() {
rootCmd.AddCommand(runCmd)
// Here you will define your flags and configuration settings.
runCmd.Flags().StringP("directory", "d", "", "Game directory")
runCmd.Flags().StringP("executable", "x", "", "Game executable")
runCmd.Flags().Int("gpu", 0, "GPU number")
runCmd.Flags().StringP("vendor", "v", "", "GPU vendor")
runCmd.Flags().IntP("height", "H", 1080, "Screen height")
runCmd.Flags().IntP("width", "W", 1920, "Screen width")
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// runCmd.PersistentFlags().String("foo", "", "A help for foo")
// viper.BindPFlag("directory", runCmd.Flags().Lookup("directory"))
// viper.BindPFlag("executable", runCmd.Flags().Lookup("executable"))
// viper.BindPFlag("gpu", runCmd.Flags().Lookup("gpu"))
// viper.BindPFlag("vendor", runCmd.Flags().Lookup("vendor"))
// viper.BindPFlag("resolution.height", runCmd.Flags().Lookup("height"))
// viper.BindPFlag("resolution.width", runCmd.Flags().Lookup("width"))
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// runCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
viper.BindPFlag("games.*.directory", runCmd.Flags().Lookup("directory"))
viper.BindPFlag("games.*.executable", runCmd.Flags().Lookup("executable"))
viper.BindPFlag("games.*.gpu", runCmd.Flags().Lookup("gpu"))
viper.BindPFlag("games.*.vendor", runCmd.Flags().Lookup("vendor"))
viper.BindPFlag("games.*.resolution.height", runCmd.Flags().Lookup("height"))
viper.BindPFlag("games.*.resolution.width", runCmd.Flags().Lookup("width"))
}