commit df25294055731ee7b72d51f89411a3e97c09c914 Author: deranjer Date: Sun Jul 19 20:42:58 2020 -0400 initial push with clean and rebuild commands diff --git a/ForlornOutcast.uproject b/ForlornOutcast.uproject new file mode 100644 index 0000000..82046c2 --- /dev/null +++ b/ForlornOutcast.uproject @@ -0,0 +1,92 @@ +{ + "FileVersion": 3, + "EngineAssociation": "4.25", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "ForlornOutcast", + "Type": "Runtime", + "LoadingPhase": "Default", + "AdditionalDependencies": [ + "Engine", + "CoreUObject", + "UFSM", + "FlockingBehaviourSystem", + "NavigationSystem", + "AIModule" + ] + }, + { + "Name": "ForlornOutcastEditor", + "Type": "Editor", + "LoadingPhase": "PostEngineInit" + } + ], + "Plugins": [ + { + "Name": "ChaosNiagara", + "Enabled": true + }, + { + "Name": "NiagaraExtras", + "Enabled": true + }, + { + "Name": "Niagara", + "Enabled": true + }, + { + "Name": "ApexDestruction", + "Enabled": true + }, + { + "Name": "UFSM", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/195d90d8cedf4b478a92036c059e2374" + }, + { + "Name": "FlockingBehaviourSystem", + "Enabled": true, + "MarketplaceURL": "https://www.unrealengine.com/marketplace/en-US/slug/flocking-behaviour-system-ideal-for-simulation-of-birds-fish-bees-and-more" + }, + { + "Name": "SteamVR", + "Enabled": false, + "SupportedTargetPlatforms": [ + "Win32", + "Win64", + "Linux", + "Mac" + ] + }, + { + "Name": "SaveExtension", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/91084f43b68047ac8da6fc88a7467936" + }, + { + "Name": "RiderLink", + "Enabled": false + }, + { + "Name": "BlockoutToolsPlugin", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/d337b97aca8947a893526964e179acfd" + }, + { + "Name": "NodeGraphAssistant", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/d9baaaeadeaf41dcb52b3f6e134e9514" + }, + { + "Name": "OculusVR", + "Enabled": false, + "SupportedTargetPlatforms": [ + "Win32", + "Win64", + "Android" + ] + } + ] +} \ No newline at end of file diff --git a/engine/compile.go b/engine/compile.go new file mode 100644 index 0000000..f03b138 --- /dev/null +++ b/engine/compile.go @@ -0,0 +1,50 @@ +package engine + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + + "golang.org/x/sys/windows/registry" +) + +func findUnrealVersionPath(version string) (string, error) { + //Attempting to build project files programattically + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\EpicGames\Unreal Engine\`+version, registry.QUERY_VALUE) + if err != nil { + return "", err + } + defer k.Close() + s, _, err := k.GetStringValue("InstalledDirectory") //Pulling the (Default) value which has the path + if err != nil { + return "", err + } + return s, nil +} + +func buildProject(version string, projectABSPath string) error { + unrealVersionPath, err := findUnrealVersionPath(version) + if err != nil { + fmt.Printf("Unable to find the path to Unreal Engine version %s, please build project files manually: %s ", version, err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + unrealVersionPath = unrealVersionPath + string(filepath.Separator) + "Engine" + string(filepath.Separator) + "Build" + string(filepath.Separator) + "BatchFiles" + string(filepath.Separator) + "Build.bat" + checkPath(unrealVersionPath) // Verifying path, will exit if fail + fmt.Println("Using Unreal Engine Path: ", unrealVersionPath) + // Manually building up the commmand + buildArgs := []string{"Development", "Win64", "-Project=" + projectABSPath, "-TargetType=Editor", "-Progress", "-NoEngineChanges", "-NoHotReloadFromIDE"} + buildFilesCMD := exec.Command(unrealVersionPath, buildArgs...) + // Making sure we write the output of the command to the command prompt + out := io.Writer(os.Stdout) + buildFilesCMD.Stdout = out + // finally running the command + err = buildFilesCMD.Run() + if err != nil { + return err + } + return nil +} diff --git a/engine/engine.go b/engine/engine.go new file mode 100644 index 0000000..846a1a7 --- /dev/null +++ b/engine/engine.go @@ -0,0 +1,98 @@ +package engine + +import ( + "bufio" + "fmt" + "os" + "path/filepath" +) + +var version = "4.25" //Need to manually set the UEVersion, don't know how to guess this + +// RebuildProject is the main function to clean up the project, generate files and build +func RebuildProject(riderEnabled bool, doBuild bool) { + _, err := os.Getwd() + if err != nil { + fmt.Println("Unable to get working dir, returned error: ", err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + projectFile, err := os.Stat("ForlornOutcast.uproject") + if err != nil { + if os.IsNotExist(err) { + fmt.Println("unable to locate project file, are you in the right directory?") + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } else { + fmt.Println("Unknown error when trying to open ForlornOutcast.uproject... exiting") + } + } + fmt.Println("Found project file... continuing ") + projectABSPath, err := filepath.Abs("ForlornOutcast.uproject") + if err != nil { + fmt.Println("Unable to generate absolute file path to project file: ", err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + checkPath(projectABSPath) // Verifying path, will exit if fail + fmt.Println("Using Project Absolute Path: ", projectABSPath) + // Setting the folders to remove //remove.go + folderRemove := []string{".vs", "Binaries", "Intermediate", "Saved"} + fmt.Println("Removing Folders...") + err = removeFolders(folderRemove) + if err != nil { + fmt.Println(err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + // Setting the files to remove //remove.go + fileRemove := []string{"ForlornOutcast.sln"} + fmt.Println("Removing Files...") + err = removeFiles(fileRemove) + if err != nil { + fmt.Println(err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + // Editing the .uproject file //jsonEdit.go + err = editProjectFile(projectFile, riderEnabled) + if err != nil { + fmt.Printf("Error reading/modifying project file: %s", err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + fmt.Println("Project file has been modified as requested") + // Generating the vs files //generateFiles.go + err = generateFiles(projectABSPath) + if err != nil { + fmt.Println("Unable to generate project files, please generate project files manually: ", err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + fmt.Println("Project Files should have been generated...") + if !doBuild { + fmt.Println("Finished, press to exit!") + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + // Building the project //compile.go + err = buildProject(version, projectABSPath) + if err != nil { + fmt.Println("Unable to build project, please build the project manually: ", err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + fmt.Println("Finished, press to exit!") + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) +} + +// checkPath is a generic function to just verify file exists +func checkPath(path string) { + _, err := os.Stat(path) + if err != nil { + fmt.Printf("Path for file %s does not appear to be correct, error: %s exiting...", path, err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } +} diff --git a/engine/generateFiles.go b/engine/generateFiles.go new file mode 100644 index 0000000..68a816b --- /dev/null +++ b/engine/generateFiles.go @@ -0,0 +1,50 @@ +package engine + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "golang.org/x/sys/windows/registry" +) + +func findUnrealPath() (string, error) { + //Attempting to generate project files programattically + k, err := registry.OpenKey(registry.CLASSES_ROOT, `Unreal.ProjectFile\DefaultIcon`, registry.QUERY_VALUE) + if err != nil { + return "", err + } + defer k.Close() + s, _, err := k.GetStringValue("") //Pulling the (Default) value which has the path + if err != nil { + return "", err + } + s = strings.Trim(s, "\"") //Trim the quotes from around the string + return s, nil +} + +func generateFiles(projectFileABSPath string) error { + // find the path to versionswitcher which is not in a specific unreal version directory since it needs to switch between versions + unrealExPath, err := findUnrealPath() + if err != nil { + fmt.Println("Unable to find the path to Unreal engine, please generate project files manually: ", err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + checkPath(unrealExPath) // Verifying path, will exit if fail + fmt.Println("Using Unreal Engine Path: ", unrealExPath) + generateArgs := []string{"/projectfiles", projectFileABSPath} + generateFilesCMD := exec.Command(unrealExPath, generateArgs...) + // var stdBuffer bytes.Buffer + // mw := io.MultiWriter(os.Stdout, &stdBuffer) + out := io.Writer(os.Stdout) + generateFilesCMD.Stdout = out + err = generateFilesCMD.Run() + if err != nil { + return err + } + return nil +} diff --git a/engine/jsonEdit.go b/engine/jsonEdit.go new file mode 100644 index 0000000..84dac7c --- /dev/null +++ b/engine/jsonEdit.go @@ -0,0 +1,64 @@ +package engine + +import ( + "encoding/json" + "io/ioutil" + "os" +) + +// This is the entire JSON File Struct so we can modify it as needed +type unrealProjectJSON struct { + FileVersion int `json:"FileVersion"` + EngineAssociation string `json:"EngineAssociation"` + Category string `json:"Category"` + Description string `json:"Description"` + Modules []struct { + Name string `json:"Name"` + Type string `json:"Type"` + LoadingPhase string `json:"LoadingPhase"` + AdditionalDependencies []string `json:"AdditionalDependencies,omitempty"` + } `json:"Modules"` + Plugins []struct { + Name string `json:"Name"` + Enabled bool `json:"Enabled"` + MarketplaceURL string `json:"MarketplaceURL,omitempty"` + SupportedTargetPlatforms []string `json:"SupportedTargetPlatforms,omitempty"` + } `json:"Plugins"` +} + +func editProjectFile(projectFile os.FileInfo, enableRider bool) error { + byteValue, err := ioutil.ReadFile(projectFile.Name()) + if err != nil { + return err + } + + var projectStruct unrealProjectJSON + json.Unmarshal([]byte(byteValue), &projectStruct) + + for i, plugin := range projectStruct.Plugins { + if plugin.Name == "OculusVR" { + projectStruct.Plugins[i].Enabled = false + + } + if plugin.Name == "SteamVR" { + projectStruct.Plugins[i].Enabled = false + + } + if plugin.Name == "RiderLink" { + if enableRider { + projectStruct.Plugins[i].Enabled = true + } else { + projectStruct.Plugins[i].Enabled = false + } + } + } + file, err := json.MarshalIndent(projectStruct, "", "\t") + if err != nil { + return err + } + err = ioutil.WriteFile(projectFile.Name(), file, 0644) + if err != nil { + return err + } + return nil +} diff --git a/engine/remove.go b/engine/remove.go new file mode 100644 index 0000000..83369ae --- /dev/null +++ b/engine/remove.go @@ -0,0 +1,39 @@ +package engine + +import ( + "bufio" + "fmt" + "os" +) + +func removeFolders(folders []string) error { + for _, folder := range folders { + if err := os.RemoveAll(folder); err != nil { + if os.IsNotExist(err) { //If it doesn't exist that is fine, we would delete it anyway + continue + } else { + fmt.Printf("Error removing %s folder, error: %s exiting...", folder, err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + } + fmt.Println("Removed Folder: ", folder) + } + return nil +} + +func removeFiles(files []string) error { + for _, file := range files { + if err := os.Remove(file); err != nil { + if os.IsNotExist(err) { //If it doesn't exist that is fine, we would delete it anyway + continue + } else { + fmt.Printf("Error removing %s file, error: %s exiting...", file, err) + bufio.NewReader(os.Stdin).ReadBytes('\n') + os.Exit(1) + } + } + fmt.Println("Removed file: ", file) + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5b3c143 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/deranjer/ForlornProjectManager + +go 1.14 + +require ( + github.com/c-bata/go-prompt v0.2.3 + github.com/mattn/go-colorable v0.1.7 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mattn/go-tty v0.0.3 // indirect + github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 // indirect + github.com/tidwall/sjson v1.1.1 // indirect + golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..29cf9d9 --- /dev/null +++ b/go.sum @@ -0,0 +1,32 @@ +github.com/c-bata/go-prompt v0.2.3 h1:jjCS+QhG/sULBhAaBdjb2PlMRVaKXQgn+4yzaauvs2s= +github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 h1:pd4YKIqCB0U7O2I4gWHgEUA2mCEOENmco0l/bM957bU= +github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA= +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= +github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U= +github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/main.exe b/main.exe new file mode 100644 index 0000000..f9f4f8e Binary files /dev/null and b/main.exe differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..042ad59 --- /dev/null +++ b/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + "os" + "strings" + + prompt "github.com/c-bata/go-prompt" + "github.com/deranjer/ForlornProjectManager/engine" +) + +func executor(in string) { + in = strings.TrimSpace(in) + + blocks := strings.Split(in, " ") + switch blocks[0] { + case "exit": + fmt.Println("Exiting!") + os.Exit(0) + + case "rebuild": + if len(blocks) > 1 { + fmt.Println("Subcommand is: ", blocks[1]) + switch blocks[1] { + case "--rider": + fmt.Println("Enabling rider plugin when cleaning") + engine.RebuildProject(true, true) + } + } else { + fmt.Println("Cleaning project with default settings...") + engine.RebuildProject(false, true) + } + case "clean": + if len(blocks) > 1 { + fmt.Println("Flag is: ", blocks[1]) + switch blocks[1] { + case "--rider": + fmt.Println("Enabling rider plugin when cleaning") + engine.RebuildProject(true, false) + } + } else { + fmt.Println("Cleaning project with default settings...") + engine.RebuildProject(false, false) + } + case "help": + fmt.Println("Use 'clean' or 'rebuild' with the optional flag '--rider' if you need the rider plugin enabled.") + fmt.Println("Example Usage: 'clean --rider' or 'rebuild --rider'.") + case "post", "put", "patch": + if len(blocks) < 2 { + fmt.Println("please set request body.") + return + } + default: + fmt.Println("Sorry, I don't understand") + } +} + +func completer(in prompt.Document) []prompt.Suggest { + s := []prompt.Suggest{ + {Text: "rebuild", Description: "cleans the project folder and regenerates all files, then builds"}, + {Text: "clean", Description: "only cleans the project folder and regenerates files, no build"}, + {Text: "help", Description: "Shows the help for Unreal Project Manager"}, + {Text: "exit", Description: "Exit the program"}, + {Text: "--rider", Description: "FLAG: Enables the rider plugin after 'clean' or 'rebuild'"}, + } + return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true) +} + +func main() { + fmt.Println("Welcome to the Unreal Project Manager, run 'clean' to run a basic clean/regenerate or 'rebuild' to do a full rebuild.") + fmt.Println("Make sure Unreal and Visual Studio are closed, this cannot run if they are open.") + fmt.Println("NOTE: By default the rider plugin is disabled on rebuild user '--rider' flag to enable it on build or clean") + fmt.Println("Type 'help' to display the full information") + p := prompt.New( + executor, + completer, + prompt.OptionPrefix(">>> "), + //prompt.OptionParser() + ) + p.Run() +}