PageRenderTime 2ms CodeModel.GetById 2ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/BusinessLogic/Games/Package.cs

https://github.com/demscode/bearded-lama
C# | 237 lines | 174 code | 21 blank | 42 comment | 5 complexity | 7bf8c3c05befbf52544a345eb78905aa MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Text;
  5using System.IO;
  6using System.Configuration;
  7using Ionic.Zip;
  8using Newtonsoft.Json;
  9using Newtonsoft.Json.Schema;
 10using Newtonsoft.Json.Linq;
 11using System.Xml.Linq;
 12
 13namespace BusinessLogic.Games
 14{
 15    /// <summary>
 16    /// The Package class handles submission, validation and updating of user game packages.
 17    /// 
 18    /// A Game Package consists of game files and a configuration file, bearded.json; configuration attributes
 19    /// are supplied by the user to give information about the available game files, game entry points and
 20    /// game metadata like gaming instructions.
 21    /// </summary>
 22    static public class Package
 23    {
 24        // json-schema.org
 25        // TODO: move as file to top level of solution, and as configuration setting
 26        static private string packageSchema = @"
 27{
 28    'description': 'Game Package schema for Bearded Lama',
 29    'type': 'object',
 30    'required': true,
 31    'properties': {
 32        'name': {
 33            'type': 'string'
 34        },
 35        'identifier': {
 36            'type': 'string'
 37        },
 38        'information': {
 39            'type': 'string'
 40        },
 41        'instruction': {
 42            'type': 'string'
 43        },
 44        'data': {
 45            'description': 'Files required for the Game',
 46            'type': 'object',
 47            'required': true,
 48            'properties': {
 49                'js': {
 50                    'type': 'array',
 51                    'items': {
 52                        'type': 'string'
 53                    },
 54                    'minItems': 1,
 55                    'required': true
 56                },
 57                'content': {
 58                    'type': 'array',
 59                    'type': 'array',
 60                    'items': {
 61                        'type': 'string'
 62                    },
 63                    'minItems': 1
 64                }
 65            }
 66        },
 67        'entry': {
 68            'description': 'Properties for the HTML entry point of the Game',
 69            'type': 'object',
 70            'required': true,
 71            'properties': {
 72                'element': {
 73                    'type': 'string',
 74                    'required': true
 75                },
 76                'elementid': {
 77                    'type': 'string',
 78                    'required': true
 79                },
 80                'height': {
 81                    'type': 'string',
 82                    'required': true
 83                },
 84                'width': {
 85                    'type': 'string',
 86                    'required': true
 87                }
 88            }
 89        }
 90    }
 91}";
 92        /// <summary>
 93        /// New games are submitted to the website and database.
 94        /// </summary>
 95        /// <param name="archivePath">The full path to a game package archive.</param>
 96        /// <param name="userid">The userId of the Uploader.</param>
 97        /// <param name="name">The Games name.</param>
 98        /// <param name="description">The Games description.</param>
 99        /// <param name="tags">The Games tags.</param>
100        /// <returns>A new instance of the Package.PackageInfo includes all properties.</returns>
101        static public PackageInfo SubmitNewPackage(string archivePath, long userid, string name, string description, string[] tags, string[] categories, bool restricted)
102        {
103            PackageInfo packageConfig;
104            using (ZipFile archive = ZipFile.Read(archivePath))
105            {
106                ZipEntry packageConfigEntry = archive["bearded-lama.json"];
107                using (TextReader packageConfigReader = new StreamReader(packageConfigEntry.OpenReader()))
108                {
109                    packageConfig = ValidateConfiguration(packageConfigReader);
110                }
111
112                // Verify given configuration 'data' paths exist in the archive
113                foreach (List<string> resourceGroup in packageConfig.Data.Values)
114                {
115                    foreach (string resource in resourceGroup)
116                    {
117                        if (!archive.ContainsEntry(resource))
118                        {
119                            throw new ZipException("Packaging error: " + resource + " not found in " + archive.Name + ".");
120                        }
121                    }
122                }
123
124                packageConfig.Name = name;
125                packageConfig.Description = description;
126
127                // insert new game information into db
128                Access.NewGame(name, description, String.Join(",", tags), String.Join(",", categories), packageConfig.ToJsonString(), userid, restricted);
129                long gameId = (long)new DataAccess.GamesTableAdapters.GamesTableAdapter().GetGameIdByGameName(name);
130                packageConfig.Id = gameId.ToString();
131                DataSets.Games.GamesRow gameRow = Access.GetGameById(gameId);
132                gameRow.publicPackage = packageConfig.ToJsonString();
133                Access.UpdateGame(gameRow); // update with newly minted games id
134
135                // unpack game into Public/gameid/*
136                string publicPath = Path.GetFullPath(ConfigurationManager.AppSettings["UploadPath"] + "/Public/" + gameId);
137                foreach (List<string> resourceGroup in packageConfig.Data.Values)
138                {
139                    foreach (string resource in resourceGroup)
140                    {
141                        archive[resource].Extract(publicPath, ExtractExistingFileAction.OverwriteSilently);
142                    }
143                }
144            }
145
146            if (packageConfig == null)
147            {
148                throw new Exception("Validation failed: Package configuration found to be null.");
149            }
150
151            return packageConfig;
152        }
153
154        static private void ExtractPackageConfig(string archivePath)
155        {
156
157        }
158
159        /// <summary>
160        /// Initialises a new instance of the Package.PackageInfo class from the specified TextReader.
161        /// </summary>
162        /// <param name="jsonReader">TextReader for a JSON package, matching define schema.</param>
163        /// <returns>A new instance of the Package.PackageInfo includes properties parsed successfully from the Json Configuration.</returns>
164        /// <exception cref="JsonSchemaException">Contains schema validation messages in e.Data["validationMessages"]</exception>
165        static public PackageInfo ValidateConfiguration(TextReader jsonReader)
166        {
167            using (JsonValidatingReader validatingReader = new JsonValidatingReader(new JsonTextReader(jsonReader)))
168            {
169                JsonSchema schema = JsonSchema.Parse(packageSchema);
170                validatingReader.Schema = schema;
171                IList<string> messages = new List<string>();
172                validatingReader.ValidationEventHandler += (o, a) => messages.Add(a.Message);
173
174                JsonSerializer serialiser = new JsonSerializer();
175                PackageInfo deserialisedPackageInfo = serialiser.Deserialize<PackageInfo>(validatingReader);
176
177                if (messages.Count != 0)
178                {
179                    JsonSchemaException e = new JsonSchemaException();
180                    e.Data.Add("validationMessages", messages);
181                    throw e;
182                }
183                else
184                {
185                    return deserialisedPackageInfo;
186                }
187            }
188        }
189
190        /// <summary>
191        /// Return a non-validated instance of PackageInfo.
192        /// </summary>
193        /// <param name="jsonReader">Reader to a JSON String.</param>
194        /// <returns>New PackageInfo instance.</returns>
195        static public PackageInfo DeserialiaseConfiguration(TextReader jsonReader)
196        {
197            return (new JsonSerializer()).Deserialize<PackageInfo>(new JsonTextReader(jsonReader));
198        }
199
200        // TODO: Create Game Package
201        // TODO: Update Game Package
202        // TODO: Update Game Information
203    }
204
205    /// <summary>
206    /// An instance of a deserialised json configuration, bearded-lama.json
207    /// </summary>
208    public class PackageInfo
209    {
210        [JsonProperty("name")]
211        public string Name { get; set; }
212
213        [JsonProperty("identifier")]
214        public string Id { get; set; }
215
216        [JsonProperty("information")]
217        public string Description { get; set; }
218
219        [JsonProperty("instruction")]
220        public string Instruction { get; set; }
221
222        [JsonProperty("data")]
223        public Dictionary<String, List<string>> Data { get; set; }
224
225        [JsonProperty("entry")]
226        public Dictionary<String, String> EntryPoint { get; set; }
227
228        /// <summary>
229        /// Returns an unindented JSON string of this PackageInfo instance.
230        /// </summary>
231        /// <returns>Unindented JSON string.</returns>
232        public string ToJsonString()
233        {
234            return JsonConvert.SerializeObject(this, Formatting.None);
235        }
236    }
237}