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