public async Task <IActionResult> AssetUploadServlet([FromForm] AssetUploadForm formAsset)
        {
            Console.WriteLine($"/pollinator/public-interface/AssetUploadServlet{Request.QueryString}");

            // the game client always sends the slurp query
            // and it's always either 0 or 1
            if (!Request.Query.ContainsKey("slurp") ||
                !int.TryParse(Request.Query["slurp"], out int slurpValue) ||
                (slurpValue != 0 && slurpValue != 1))
            {
                return(Ok());
            }

            Int64 parentId = 0;

            // the game sometimes sends a parent id,
            // make sure we can parse it
            if (Request.Query.ContainsKey("parent") &&
                !Int64.TryParse(Request.Query["parent"], out parentId))
            {
                return(Ok());
            }

            SporeServerAsset parentAsset = null;

            // when parentId is not 0,
            // try to find parent asset
            if (parentId != 0)
            {
                parentAsset = await _assetManager.FindByIdAsync(parentId);
            }

            // the game always sends the type id
            // make sure we can parse it
            // and that's it a valid id
            if (!Int64.TryParse(formAsset.TypeId.TrimStart('0', 'x'),
                                NumberStyles.HexNumber,
                                null,
                                out Int64 typeId) ||
                !Enum.IsDefined(typeof(SporeAssetType), typeId))
            {
                Console.WriteLine($"invalid type id: {typeId}");
                return(Ok());
            }

            var user = await _userManager.GetUserAsync(User);

            // make sure the requested assetId is the user's nextAssetId
            if (user.NextAssetId != formAsset.AssetId)
            {
                return(Ok());
            }

            var asset = await _assetManager.FindByIdAsync(formAsset.AssetId);

            // make sure the asset exists and
            // make sure it isn't already used
            if (asset == null ||
                asset.Used)
            {
                return(Ok());
            }

            // make sure the asset doesn't go over any limits
            if ((formAsset.Description != null &&
                 formAsset.Description.Length > 256) ||
                (formAsset.ModelData != null &&
                 formAsset.ModelData.FileName.Length > 32) ||
                (formAsset.Tags != null &&
                 formAsset.Tags.Length > 256))
            {
                return(Ok());
            }

            // save the asset
            if (!await _assetManager.AddAsync(formAsset,
                                              asset,
                                              parentAsset,
                                              (slurpValue == 1),
                                              (SporeAssetType)typeId))
            {
                return(StatusCode(500));
            }

            return(Ok());
        }
        public async Task <bool> AddAsync(AssetUploadForm form, SporeServerAsset asset, SporeServerAsset parentAsset, bool slurped, SporeAssetType type)
        {
            try
            {
                var files = GetAssetFiles(form.AssetId);

                // load, validate & deserialize SporeModel
                SporeModel model    = null;
                string     modelXml = null;
                using (Stream modelStream = OpenFormFileStream(form.ModelData))
                {
                    if (!LoadSporeModel(modelStream, out model))
                    {
                        throw new Exception("LoadSporeModel Failed");
                    }
                    if (!ValidateSporeModel(model))
                    {
                        throw new Exception("ValidateSporeModel Failed");
                    }
                    if (!DeserializeSporeModel(model, out modelXml))
                    {
                        throw new Exception("DeserializeSporeModel Failed");
                    }
                }

                // save files
                File.WriteAllText(files.ModelFile, modelXml);
                await SaveFormFile(form.ThumbnailData, files.ThumbFile);

                if (form.ImageData != null)
                {
                    await SaveFormFile(form.ImageData, files.ImageFile);
                }
                if (form.ImageData_2 != null)
                {
                    await SaveFormFile(form.ImageData_2, files.ImageFile2);
                }
                if (form.ImageData_3 != null)
                {
                    await SaveFormFile(form.ImageData_3, files.ImageFile3);
                }
                if (form.ImageData_4 != null)
                {
                    await SaveFormFile(form.ImageData_4, files.ImageFile4);
                }

                // update database
                asset.Used            = true;
                asset.Timestamp       = DateTime.Now;
                asset.OriginalAssetId = 0;
                asset.ParentAssetId   = 0;
                if (parentAsset != null)
                {
                    // when original asset id of parent is 0,
                    // the parent id is the original asset id
                    // else follow the original asset id specified
                    if (parentAsset.OriginalAssetId == 0)
                    {
                        asset.OriginalAssetId = parentAsset.AssetId;
                    }
                    else
                    {
                        asset.OriginalAssetId = parentAsset.OriginalAssetId;
                    }
                    asset.ParentAssetId = parentAsset.AssetId;
                }
                asset.Name = form.ModelData.FileName;

                var tags = new List <SporeServerAssetTag>();
                if (form.Tags != null)
                {
                    foreach (string tagString in form.Tags.Split(","))
                    {
                        string trimmedTagString = tagString.TrimStart().TrimEnd();

                        tags.Add(new SporeServerAssetTag()
                        {
                            Asset = asset,
                            Tag   = trimmedTagString
                        });
                    }
                }
                asset.Tags = tags;

                var traits = new List <SporeServerAssetTrait>();
                if (form.TraitGuids != null)
                {
                    foreach (string traitString in form.TraitGuids.Split(","))
                    {
                        string trimmedTraitString = traitString.TrimStart()
                                                    .TrimStart('0', 'x')
                                                    .TrimEnd();

                        Int64 traitType = Int64.Parse(trimmedTraitString, NumberStyles.HexNumber);

                        // make sure the trait id is valid
                        if (!Enum.IsDefined(typeof(SporeAssetTraitType), traitType))
                        {
                            throw new Exception($"Invalid Trait Id: {traitType}");
                        }

                        traits.Add(new SporeServerAssetTrait()
                        {
                            Asset     = asset,
                            TraitType = (SporeAssetTraitType)traitType
                        });
                    }
                }
                asset.Traits = traits;

                asset.Description = form.Description;
                asset.Size        = form.ThumbnailData.Length;
                asset.Slurped     = slurped;
                // TODO, put this in a struct or whatever?
                asset.ModelFileUrl  = GetRelativeUrlFromPath(files.ModelFile);
                asset.ThumbFileUrl  = GetRelativeUrlFromPath(files.ThumbFile);
                asset.ImageFileUrl  = GetRelativeUrlFromPath(files.ImageFile);
                asset.ImageFile2Url = GetRelativeUrlFromPath(files.ImageFile2);
                asset.ImageFile3Url = GetRelativeUrlFromPath(files.ImageFile3);
                asset.ImageFile4Url = GetRelativeUrlFromPath(files.ImageFile4);
                asset.ModelType     = (SporeModelType)model.Properties.ModelType;
                asset.Type          = type;
                _context.Assets.Update(asset);
                await _context.SaveChangesAsync();

                _logger.LogInformation($"AddAsync: Added Asset {asset.AssetId}");
                return(true);
            }
            catch (Exception e)
            {
                _logger.LogError($"AddAsync: Failed To Add Asset {asset.AssetId}: {e}");
                return(false);
            }
        }