Pārlūkot izejas kodu

Improved egg importing so it should be able to import any egg now

Masu Baumgartner 1 gadu atpakaļ
vecāks
revīzija
8dc37525ce

+ 182 - 24
Moonlight/Features/Servers/Helpers/ImageConversionHelper.cs

@@ -9,6 +9,7 @@ using Moonlight.Features.Servers.Entities;
 using Moonlight.Features.Servers.Models;
 using Moonlight.Features.Servers.Models;
 using Moonlight.Features.Servers.Models.Json;
 using Moonlight.Features.Servers.Models.Json;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
 
 
 namespace Moonlight.Features.Servers.Helpers;
 namespace Moonlight.Features.Servers.Helpers;
 
 
@@ -30,7 +31,7 @@ public class ImageConversionHelper
             .Include(x => x.Variables)
             .Include(x => x.Variables)
             .First(x => x.Id == image.Id);
             .First(x => x.Id == image.Id);
 
 
-        
+
         var model = new ImageJson()
         var model = new ImageJson()
         {
         {
             Name = imageWithData.Name,
             Name = imageWithData.Name,
@@ -54,7 +55,7 @@ public class ImageConversionHelper
 
 
         foreach (var variable in imageWithData.Variables)
         foreach (var variable in imageWithData.Variables)
             model.Variables.Add(Mapper.Map<ImageJson.ImageVariable>(variable));
             model.Variables.Add(Mapper.Map<ImageJson.ImageVariable>(variable));
-        
+
         foreach (var dockerImage in imageWithData.DockerImages)
         foreach (var dockerImage in imageWithData.DockerImages)
             model.DockerImages.Add(Mapper.Map<ImageJson.ImageDockerImage>(dockerImage));
             model.DockerImages.Add(Mapper.Map<ImageJson.ImageDockerImage>(dockerImage));
 
 
@@ -88,20 +89,21 @@ public class ImageConversionHelper
 
 
         foreach (var variable in model.Variables)
         foreach (var variable in model.Variables)
             result.Variables.Add(Mapper.Map<ServerImageVariable>(variable));
             result.Variables.Add(Mapper.Map<ServerImageVariable>(variable));
-        
+
         foreach (var dockerImage in model.DockerImages)
         foreach (var dockerImage in model.DockerImages)
             result.DockerImages.Add(Mapper.Map<ServerDockerImage>(dockerImage));
             result.DockerImages.Add(Mapper.Map<ServerDockerImage>(dockerImage));
 
 
         return Task.FromResult(result);
         return Task.FromResult(result);
     }
     }
-
-    public Task<ServerImage> ImportFromEggJson(string json)
+    
+    // Old import function which used the microsoft json parsing
+    public Task<ServerImage> ImportFromEggJson_Old(string json)
     {
     {
         var fixedJson = json;
         var fixedJson = json;
 
 
         fixedJson = fixedJson.Replace("\\/", "/");
         fixedJson = fixedJson.Replace("\\/", "/");
-        
-        // Note: We use microsofts config system here as its dynamic and probably the best parsing method to use here
+
+        // Note: We use microsofts config system instead of newtonsoft.json as its dynamic and probably the best parsing method to use here
         var eggData = new ConfigurationBuilder()
         var eggData = new ConfigurationBuilder()
             .AddJsonString(fixedJson)
             .AddJsonString(fixedJson)
             .Build();
             .Build();
@@ -114,7 +116,11 @@ public class ImageConversionHelper
         result.Author = eggData["author"] ?? "Author was missing";
         result.Author = eggData["author"] ?? "Author was missing";
         result.StartupCommand = eggData["startup"] ?? "Startup was missing";
         result.StartupCommand = eggData["startup"] ?? "Startup was missing";
         result.StopCommand = eggData.GetSection("config")["stop"] ?? "Stop command was missing";
         result.StopCommand = eggData.GetSection("config")["stop"] ?? "Stop command was missing";
-        
+
+        // Some weird eggs use ^^C in as a stop command, so we need to handle this as well
+        // because moonlight handles power signals correctly, wings does/did not
+        result.StopCommand = result.StopCommand.Replace("^^C", "^C");
+
         // Startup detection
         // Startup detection
         var startupDetectionData = new ConfigurationBuilder()
         var startupDetectionData = new ConfigurationBuilder()
             .AddJsonString(eggData.GetSection("config")["startup"] ?? "{}")
             .AddJsonString(eggData.GetSection("config")["startup"] ?? "{}")
@@ -122,14 +128,14 @@ public class ImageConversionHelper
 
 
         // Node Regex: As the online detection uses regex, we want to escape any special chars from egg imports
         // Node Regex: As the online detection uses regex, we want to escape any special chars from egg imports
         // as eggs dont use regex and as such may contain characters which regex uses as meta characters.
         // as eggs dont use regex and as such may contain characters which regex uses as meta characters.
-        // Without this escaping, many startup detection string wont work
+        // Without this escaping, many startup detection strings wont work
         result.OnlineDetection = Regex.Escape(startupDetectionData["done"] ?? "Online detection was missing");
         result.OnlineDetection = Regex.Escape(startupDetectionData["done"] ?? "Online detection was missing");
 
 
         // Docker image method 1:
         // Docker image method 1:
         // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/mindustry/egg-mindustry.json
         // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/mindustry/egg-mindustry.json
         if (eggData["image"] != null)
         if (eggData["image"] != null)
         {
         {
-            result.DockerImages.Add(new ()
+            result.DockerImages.Add(new()
             {
             {
                 Name = eggData["image"]!,
                 Name = eggData["image"]!,
                 AutoPull = true,
                 AutoPull = true,
@@ -143,14 +149,14 @@ public class ImageConversionHelper
 
 
         foreach (var dockerImage in dockerImagesSection.GetChildren())
         foreach (var dockerImage in dockerImagesSection.GetChildren())
         {
         {
-            result.DockerImages.Add(new ()
+            result.DockerImages.Add(new()
             {
             {
                 Name = dockerImage.Value ?? "Docker image name was missing",
                 Name = dockerImage.Value ?? "Docker image name was missing",
                 AutoPull = true,
                 AutoPull = true,
                 DisplayName = dockerImage.Key
                 DisplayName = dockerImage.Key
             });
             });
         }
         }
-        
+
         // Docker image method 3:
         // Docker image method 3:
         // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/cuberite/egg-cuberite.json
         // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/cuberite/egg-cuberite.json
         var dockerImagesList = eggData.GetValue<List<string>>("images");
         var dockerImagesList = eggData.GetValue<List<string>>("images");
@@ -159,7 +165,7 @@ public class ImageConversionHelper
         {
         {
             foreach (var imageName in dockerImagesList)
             foreach (var imageName in dockerImagesList)
             {
             {
-                result.DockerImages.Add(new ()
+                result.DockerImages.Add(new()
                 {
                 {
                     Name = imageName,
                     Name = imageName,
                     AutoPull = true,
                     AutoPull = true,
@@ -167,14 +173,14 @@ public class ImageConversionHelper
                 });
                 });
             }
             }
         }
         }
-        
+
         // Parse config
         // Parse config
         var parseConfigData = new ConfigurationBuilder()
         var parseConfigData = new ConfigurationBuilder()
             .AddJsonString(eggData.GetSection("config")["files"] ?? "{}")
             .AddJsonString(eggData.GetSection("config")["files"] ?? "{}")
             .Build();
             .Build();
 
 
         var parseConfigModels = new List<ServerParseConfig>();
         var parseConfigModels = new List<ServerParseConfig>();
-        
+
         foreach (var fileSection in parseConfigData.GetChildren())
         foreach (var fileSection in parseConfigData.GetChildren())
         {
         {
             var model = new ServerParseConfig()
             var model = new ServerParseConfig()
@@ -189,20 +195,23 @@ public class ImageConversionHelper
 
 
                 valueWithChecks = valueWithChecks.Replace("server.build.default.port", "SERVER_PORT");
                 valueWithChecks = valueWithChecks.Replace("server.build.default.port", "SERVER_PORT");
                 valueWithChecks = valueWithChecks.Replace("server.build.env.", "");
                 valueWithChecks = valueWithChecks.Replace("server.build.env.", "");
-                
+
                 model.Configuration.Add(findSection.Key, valueWithChecks);
                 model.Configuration.Add(findSection.Key, valueWithChecks);
             }
             }
-            
+
             parseConfigModels.Add(model);
             parseConfigModels.Add(model);
         }
         }
 
 
         result.ParseConfiguration = JsonConvert.SerializeObject(parseConfigModels);
         result.ParseConfiguration = JsonConvert.SerializeObject(parseConfigModels);
-        
+
         // Installation
         // Installation
-        result.InstallShell = "/bin/" + (eggData.GetSection("scripts").GetSection("installation")["entrypoint"] ?? "Install shell was missing");
-        result.InstallScript = eggData.GetSection("scripts").GetSection("installation")["script"] ?? "Install script was missing";
-        result.InstallDockerImage = eggData.GetSection("scripts").GetSection("installation")["container"] ?? "Install script was missing";
-        
+        result.InstallShell = "/bin/" + (eggData.GetSection("scripts").GetSection("installation")["entrypoint"] ??
+                                         "Install shell was missing");
+        result.InstallScript = eggData.GetSection("scripts").GetSection("installation")["script"] ??
+                               "Install script was missing";
+        result.InstallDockerImage = eggData.GetSection("scripts").GetSection("installation")["container"] ??
+                                    "Install script was missing";
+
         // Variables
         // Variables
         foreach (var variableSection in eggData.GetSection("variables").GetChildren())
         foreach (var variableSection in eggData.GetSection("variables").GetChildren())
         {
         {
@@ -225,10 +234,159 @@ public class ImageConversionHelper
                 variable.AllowView = variableSection.GetValue<int>("user_viewable") == 1;
                 variable.AllowView = variableSection.GetValue<int>("user_viewable") == 1;
                 variable.AllowEdit = variableSection.GetValue<int>("user_editable") == 1;
                 variable.AllowEdit = variableSection.GetValue<int>("user_editable") == 1;
             }
             }
-            
+
+            result.Variables.Add(variable);
+        }
+
+        return Task.FromResult(result);
+    }
+
+    public Task<ServerImage> ImportFromEggJson(string json)
+    {
+        // Prepare json
+        var fixedJson = json;
+        fixedJson = fixedJson.Replace("\\/", "/");
+
+        // Prepare result object and set moonlight native fields
+        var result = new ServerImage();
+
+        result.AllocationsNeeded = 1;
+        result.AllowDockerImageChange = true;
+
+        //
+        var egg = JObject.Parse(fixedJson);
+
+        result.AllocationsNeeded = 1; // We cannot convert this value as its moonlight native
+
+        // Basic values
+        result.Name = egg["name"]?.Value<string>() ?? "Name was missing";
+        result.Author = egg["author"]?.Value<string>() ?? "Author was missing";
+        result.StartupCommand = egg["startup"]?.Value<string>() ?? "Startup was missing";
+        result.StopCommand = egg["config"]?["stop"]?.Value<string>() ?? "Stop command was missing";
+
+        // Some weird eggs use ^^C in as a stop command, so we need to handle this as well
+        // because moonlight handles power signals correctly, wings does/did not
+        result.StopCommand = result.StopCommand.Replace("^^C", "^C");
+
+        // Startup detection
+        var startup = JObject.Parse(egg["config"]?["startup"]?.Value<string>() ?? "{}");
+
+        // Node Regex: As the online detection uses regex, we want to escape any special chars from egg imports
+        // as eggs dont use regex and as such may contain characters which regex uses as meta characters.
+        // Without this escaping, many startup detection strings wont work
+        result.OnlineDetection = Regex.Escape(startup["done"]?.Value<string>() ?? "Online detection was missing");
+
+        // Docker images
+
+        // Docker image method 1:
+        // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/mindustry/egg-mindustry.json
+        if (egg["image"] != null)
+        {
+            result.DockerImages.Add(new()
+            {
+                Name = egg["image"]?.Value<string>() ?? "Docker image not specified",
+                AutoPull = true,
+                DisplayName = egg["image"]?.Value<string>() ?? "Docker image not specified"
+            });
+        }
+
+        // Docker image method 2:
+        // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/cuberite/egg-cuberite.json
+        if (egg["images"] != null)
+        {
+            var images = egg["images"]?.ToObject<JArray>() ?? JArray.Parse("[]");
+
+            foreach (var imageName in images)
+            {
+                result.DockerImages.Add(new()
+                {
+                    Name = imageName.Value<string>() ?? "Docker image name not found",
+                    AutoPull = true,
+                    DisplayName = imageName.Value<string>() ?? "Docker image name not found",
+                });
+            }
+        }
+
+        // Docker image method 3:
+        // Reference egg: https://github.com/parkervcp/eggs/blob/master/game_eggs/minecraft/java/paper/egg-paper.json
+        if (egg["docker_images"] != null)
+        {
+            var images = egg["docker_images"]?.ToObject<JObject>() ?? JObject.Parse("{}");
+
+            foreach (var kvp in images)
+            {
+                result.DockerImages.Add(new()
+                {
+                    Name = kvp.Value?.Value<string>() ?? kvp.Key,
+                    AutoPull = true,
+                    DisplayName = kvp.Key
+                });
+            }
+        }
+
+        // Parse config
+        var parseConfig = JObject.Parse(egg["config"]?["files"]?.Value<string>() ?? "{}");
+        var parseConfigModels = new List<ServerParseConfig>();
+
+        foreach (var config in parseConfig)
+        {
+            var model = new ServerParseConfig()
+            {
+                File = config.Key,
+                Type = config.Value?["parser"]?.Value<string>() ?? "parser was missing"
+            };
+
+            if (config.Value?["find"] == null)
+                continue;
+
+            foreach (var findSection in config.Value!["find"]!.ToObject<JObject>() ?? JObject.Parse("{}"))
+            {
+                var valueWithChecks = findSection.Value?.Value<string>() ?? "Find value was null";
+
+                valueWithChecks = valueWithChecks.Replace("server.build.default.port", "SERVER_PORT");
+                valueWithChecks = valueWithChecks.Replace("server.build.env.", "");
+
+                model.Configuration.Add(findSection.Key, valueWithChecks);
+            }
+
+            parseConfigModels.Add(model);
+        }
+
+        result.ParseConfiguration = JsonConvert.SerializeObject(parseConfigModels);
+
+        // Installation
+        var installation = egg["scripts"]?["installation"] ?? JObject.Parse("{}");
+
+        result.InstallShell = "/bin/" + installation.Value<string>("entrypoint") ?? "Install shell was missing";
+        result.InstallScript = installation.Value<string>("script") ?? "Install script was missing";
+        result.InstallDockerImage = installation.Value<string>("container") ?? "Install container was missing";
+
+        // Variables
+        foreach (var variableSection in egg["variables"]?.Children() ?? JEnumerable<JToken>.Empty)
+        {
+            var variable = new ServerImageVariable()
+            {
+                DisplayName = variableSection.Value<string>("name") ?? "Name was missing",
+                Description = variableSection.Value<string>("description") ?? "Description was missing",
+                Key = variableSection.Value<string>("env_variable") ?? "Environment variable was missing",
+                DefaultValue = variableSection.Value<string>("default_value") ?? "Default value was missing",
+            };
+
+            // Check if it is a bool value
+            if (bool.TryParse(variableSection["user_viewable"]?.Value<string>(), out _))
+            {
+                variable.AllowView = variableSection.Value<bool>("user_viewable");
+                variable.AllowEdit = variableSection.Value<bool>("user_editable");
+            }
+            else
+            {
+                variable.AllowView = variableSection.Value<int>("user_viewable") == 1;
+                variable.AllowEdit = variableSection.Value<int>("user_editable") == 1;
+            }
+
             result.Variables.Add(variable);
             result.Variables.Add(variable);
         }
         }
-        
+
         return Task.FromResult(result);
         return Task.FromResult(result);
     }
     }
 }
 }