38f1ddb...178a853: v0.8.9.1, removed content folder
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using Facepunch.Steamworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -9,46 +11,102 @@ namespace Barotrauma
|
||||
public enum ContentType
|
||||
{
|
||||
None,
|
||||
Submarine,
|
||||
Jobs,
|
||||
Item,
|
||||
Character,
|
||||
Structure,
|
||||
ItemAssembly,
|
||||
Character,
|
||||
Structure,
|
||||
Outpost,
|
||||
Text,
|
||||
Executable,
|
||||
ServerExecutable,
|
||||
LocationTypes,
|
||||
LocationTypes,
|
||||
MapGenerationParameters,
|
||||
LevelGenerationParameters,
|
||||
RandomEvents,
|
||||
LevelObjectPrefabs,
|
||||
RandomEvents,
|
||||
Missions,
|
||||
BackgroundCreaturePrefabs, BackgroundSpritePrefabs,
|
||||
BackgroundCreaturePrefabs,
|
||||
Sounds,
|
||||
RuinConfig,
|
||||
Particles,
|
||||
Decals
|
||||
Decals,
|
||||
NPCConversations,
|
||||
Afflictions,
|
||||
Tutorials,
|
||||
UIStyle
|
||||
}
|
||||
|
||||
public class ContentPackage
|
||||
{
|
||||
|
||||
public static string Folder = "Data/ContentPackages/";
|
||||
|
||||
public static List<ContentPackage> list = new List<ContentPackage>();
|
||||
|
||||
|
||||
string name;
|
||||
|
||||
public string Name
|
||||
public static List<ContentPackage> List = new List<ContentPackage>();
|
||||
|
||||
//these types of files are included in the MD5 hash calculation,
|
||||
//meaning that the players must have the exact same files to play together
|
||||
private static HashSet<ContentType> multiplayerIncompatibleContent = new HashSet<ContentType>
|
||||
{
|
||||
get { return name; }
|
||||
ContentType.Jobs,
|
||||
ContentType.Item,
|
||||
ContentType.Character,
|
||||
ContentType.Structure,
|
||||
ContentType.LocationTypes,
|
||||
ContentType.MapGenerationParameters,
|
||||
ContentType.LevelGenerationParameters,
|
||||
ContentType.Missions,
|
||||
ContentType.LevelObjectPrefabs,
|
||||
ContentType.RuinConfig,
|
||||
ContentType.Outpost,
|
||||
ContentType.Afflictions
|
||||
};
|
||||
|
||||
//at least one file of each these types is required in core content packages
|
||||
private static HashSet<ContentType> corePackageRequiredFiles = new HashSet<ContentType>
|
||||
{
|
||||
ContentType.Jobs,
|
||||
ContentType.Item,
|
||||
ContentType.Character,
|
||||
ContentType.Structure,
|
||||
ContentType.Outpost,
|
||||
ContentType.Text,
|
||||
ContentType.Executable,
|
||||
ContentType.ServerExecutable,
|
||||
ContentType.LocationTypes,
|
||||
ContentType.MapGenerationParameters,
|
||||
ContentType.LevelGenerationParameters,
|
||||
ContentType.RandomEvents,
|
||||
ContentType.Missions,
|
||||
ContentType.BackgroundCreaturePrefabs,
|
||||
ContentType.RuinConfig,
|
||||
ContentType.NPCConversations,
|
||||
ContentType.Afflictions,
|
||||
ContentType.UIStyle
|
||||
};
|
||||
|
||||
public static IEnumerable<ContentType> CorePackageRequiredFiles
|
||||
{
|
||||
get { return corePackageRequiredFiles; }
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Path
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Md5Hash md5Hash;
|
||||
public string SteamWorkshopUrl;
|
||||
|
||||
public bool HideInWorkshopMenu
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Md5Hash md5Hash;
|
||||
public Md5Hash MD5hash
|
||||
{
|
||||
get
|
||||
@@ -58,108 +116,194 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public List<ContentFile> files;
|
||||
//core packages are content packages that are required for the game to work
|
||||
//e.g. they include the executable, some location types, level generation params and other files the game won't work without
|
||||
//one (and only one) core package must always be selected
|
||||
public bool CorePackage
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Version GameVersion
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<ContentFile> Files;
|
||||
|
||||
public bool HasMultiplayerIncompatibleContent
|
||||
{
|
||||
get { return Files.Any(f => multiplayerIncompatibleContent.Contains(f.Type)); }
|
||||
}
|
||||
|
||||
private ContentPackage()
|
||||
{
|
||||
files = new List<ContentFile>();
|
||||
Files = new List<ContentFile>();
|
||||
}
|
||||
|
||||
public ContentPackage(string filePath)
|
||||
public ContentPackage(string filePath, string setPath = "")
|
||||
: this()
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
|
||||
Path = filePath;
|
||||
Path = setPath == string.Empty ? filePath : setPath;
|
||||
|
||||
if (doc==null)
|
||||
if (doc?.Root == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't load content package \""+filePath+"\"!");
|
||||
DebugConsole.ThrowError("Couldn't load content package \"" + filePath + "\"!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
name = doc.Root.GetAttributeString("name", "");
|
||||
Name = doc.Root.GetAttributeString("name", "");
|
||||
HideInWorkshopMenu = doc.Root.GetAttributeBool("hideinworkshopmenu", false);
|
||||
CorePackage = doc.Root.GetAttributeBool("corepackage", false);
|
||||
SteamWorkshopUrl = doc.Root.GetAttributeString("steamworkshopurl", "");
|
||||
GameVersion = new Version(doc.Root.GetAttributeString("gameversion", "0.0.0.0"));
|
||||
|
||||
List<string> errorMsgs = new List<string>();
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
ContentType type;
|
||||
if (!Enum.TryParse(subElement.Name.ToString(), true, out type))
|
||||
if (!Enum.TryParse(subElement.Name.ToString(), true, out ContentType type))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \""+name+"\" - \""+subElement.Name.ToString()+"\" is not a valid content type.");
|
||||
continue;
|
||||
errorMsgs.Add("Error in content package \"" + Name + "\" - \"" + subElement.Name.ToString() + "\" is not a valid content type.");
|
||||
type = ContentType.None;
|
||||
}
|
||||
Files.Add(new ContentFile(subElement.GetAttributeString("file", ""), type));
|
||||
}
|
||||
|
||||
bool compatible = IsCompatible();
|
||||
//If we know that the package is not compatible, don't display error messages.
|
||||
if (compatible)
|
||||
{
|
||||
foreach (string errorMsg in errorMsgs)
|
||||
{
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
}
|
||||
files.Add(new ContentFile(subElement.GetAttributeString("file", ""), type));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return name;
|
||||
return Name;
|
||||
}
|
||||
|
||||
public static ContentPackage CreatePackage(string name)
|
||||
public bool IsCompatible()
|
||||
{
|
||||
ContentPackage newPackage = new ContentPackage("Content/Data/"+name);
|
||||
newPackage.name = name;
|
||||
newPackage.Path = Folder + name;
|
||||
list.Add(newPackage);
|
||||
if (Files.All(f => f.Type == ContentType.Submarine))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//content package compatibility checks were added in 0.9
|
||||
//0.9 is not compatible with older content packages
|
||||
if (GameVersion < new Version(0, 9))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//do additional checks here if later versions add changes that break compatibility
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ContainsRequiredCorePackageFiles()
|
||||
{
|
||||
return corePackageRequiredFiles.All(fileType => Files.Any(file => file.Type == fileType));
|
||||
}
|
||||
public bool ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes)
|
||||
{
|
||||
missingContentTypes = new List<ContentType>();
|
||||
foreach (ContentType contentType in corePackageRequiredFiles)
|
||||
{
|
||||
if (!Files.Any(file => file.Type == contentType))
|
||||
{
|
||||
missingContentTypes.Add(contentType);
|
||||
}
|
||||
}
|
||||
return missingContentTypes.Count == 0;
|
||||
}
|
||||
|
||||
public static ContentPackage CreatePackage(string name, string path, bool corePackage)
|
||||
{
|
||||
ContentPackage newPackage = new ContentPackage()
|
||||
{
|
||||
Name = name,
|
||||
Path = path,
|
||||
CorePackage = corePackage,
|
||||
GameVersion = GameMain.Version
|
||||
};
|
||||
|
||||
return newPackage;
|
||||
}
|
||||
|
||||
public ContentFile AddFile(string path, ContentType type)
|
||||
{
|
||||
if (files.Find(file => file.path == path && file.type == type) != null) return null;
|
||||
if (Files.Find(file => file.Path == path && file.Type == type) != null) return null;
|
||||
|
||||
ContentFile cf = new ContentFile(path, type);
|
||||
files.Add(cf);
|
||||
Files.Add(cf);
|
||||
|
||||
return cf;
|
||||
}
|
||||
|
||||
public void RemoveFile(ContentFile file)
|
||||
{
|
||||
files.Remove(file);
|
||||
Files.Remove(file);
|
||||
}
|
||||
|
||||
public void Save(string filePath)
|
||||
{
|
||||
XDocument doc = new XDocument();
|
||||
doc.Add(new XElement("contentpackage",
|
||||
new XAttribute("name", name),
|
||||
new XAttribute("path", Path)));
|
||||
|
||||
foreach (ContentFile file in files)
|
||||
doc.Add(new XElement("contentpackage",
|
||||
new XAttribute("name", Name),
|
||||
new XAttribute("path", Path),
|
||||
new XAttribute("corepackage", CorePackage)));
|
||||
|
||||
|
||||
doc.Root.Add(new XAttribute("gameversion", GameVersion.ToString()));
|
||||
|
||||
if (!string.IsNullOrEmpty(SteamWorkshopUrl))
|
||||
{
|
||||
doc.Root.Add(new XElement(file.type.ToString(), new XAttribute("file", file.path)));
|
||||
doc.Root.Add(new XAttribute("steamworkshopurl", SteamWorkshopUrl));
|
||||
}
|
||||
|
||||
doc.Save(System.IO.Path.Combine(filePath, name+".xml"));
|
||||
|
||||
foreach (ContentFile file in Files)
|
||||
{
|
||||
doc.Root.Add(new XElement(file.Type.ToString(), new XAttribute("file", file.Path)));
|
||||
}
|
||||
|
||||
doc.Save(filePath);
|
||||
}
|
||||
|
||||
private void CalculateHash()
|
||||
public void CalculateHash(bool logging = false)
|
||||
{
|
||||
List<byte[]> hashes = new List<byte[]>();
|
||||
|
||||
var md5 = MD5.Create();
|
||||
foreach (ContentFile file in files)
|
||||
{
|
||||
if (file.type == ContentType.Executable || file.type == ContentType.ServerExecutable) continue;
|
||||
|
||||
try
|
||||
if (logging)
|
||||
{
|
||||
DebugConsole.NewMessage("****************************** Calculating cp hash " + Name);
|
||||
}
|
||||
|
||||
foreach (ContentFile file in Files)
|
||||
{
|
||||
if (!multiplayerIncompatibleContent.Contains(file.Type)) continue;
|
||||
|
||||
try
|
||||
{
|
||||
using (var stream = File.OpenRead(file.path))
|
||||
var hash = CalculateFileHash(file);
|
||||
if (logging)
|
||||
{
|
||||
hashes.Add(md5.ComputeHash(stream));
|
||||
}
|
||||
var fileMd5 = new Md5Hash(hash);
|
||||
DebugConsole.NewMessage(" " + file.Path + ": " + fileMd5.ShortHash);
|
||||
}
|
||||
hashes.Add(hash);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while calculating content package hash: ", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[hashes.Count * 16];
|
||||
@@ -169,18 +313,107 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
md5Hash = new Md5Hash(bytes);
|
||||
if (logging)
|
||||
{
|
||||
DebugConsole.NewMessage("****************************** Package hash: " + md5Hash.ShortHash);
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetFilesOfType(ContentType type)
|
||||
private byte[] CalculateFileHash(ContentFile file)
|
||||
{
|
||||
List<ContentFile> contentFiles = files.FindAll(f => f.type == type);
|
||||
var md5 = MD5.Create();
|
||||
|
||||
List<string> filePaths = new List<string>();
|
||||
foreach (ContentFile contentFile in contentFiles)
|
||||
List<string> filePaths = new List<string> { file.Path };
|
||||
List<byte> data = new List<byte>();
|
||||
|
||||
switch (file.Type)
|
||||
{
|
||||
filePaths.Add(contentFile.path);
|
||||
case ContentType.Character:
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
|
||||
string speciesName = doc.Root.GetAttributeString("name", "");
|
||||
filePaths.Add(RagdollParams.GetDefaultFile(speciesName));
|
||||
foreach (AnimationType animationType in Enum.GetValues(typeof(AnimationType)))
|
||||
{
|
||||
filePaths.Add(AnimationParams.GetDefaultFile(speciesName, animationType));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return filePaths;
|
||||
|
||||
foreach (string filePath in filePaths)
|
||||
{
|
||||
if (!File.Exists(filePath)) continue;
|
||||
using (var stream = File.OpenRead(filePath))
|
||||
{
|
||||
byte[] fileData = new byte[stream.Length];
|
||||
stream.Read(fileData, 0, (int)stream.Length);
|
||||
if (filePath.EndsWith(".xml", true, System.Globalization.CultureInfo.InvariantCulture))
|
||||
{
|
||||
string text = System.Text.Encoding.UTF8.GetString(fileData);
|
||||
text = text.Replace("\n", "").Replace("\r", "");
|
||||
fileData = System.Text.Encoding.UTF8.GetBytes(text);
|
||||
}
|
||||
data.AddRange(fileData);
|
||||
}
|
||||
}
|
||||
return md5.ComputeHash(data.ToArray());
|
||||
}
|
||||
|
||||
public static string GetFileExtension(ContentType contentType)
|
||||
{
|
||||
switch (contentType)
|
||||
{
|
||||
case ContentType.Executable:
|
||||
case ContentType.ServerExecutable:
|
||||
return ".exe";
|
||||
default:
|
||||
return ".xml";
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsModFilePathAllowed(ContentFile contentFile)
|
||||
{
|
||||
string path = contentFile.Path;
|
||||
while (true)
|
||||
{
|
||||
string temp = System.IO.Path.GetDirectoryName(path);
|
||||
if (string.IsNullOrEmpty(temp)) { break; }
|
||||
path = temp;
|
||||
}
|
||||
switch (contentFile.Type)
|
||||
{
|
||||
case ContentType.Submarine:
|
||||
return path == "Submarines";
|
||||
default:
|
||||
return path == "Mods";
|
||||
}
|
||||
}
|
||||
public static bool IsModFilePathAllowed(string path)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string temp = System.IO.Path.GetDirectoryName(path);
|
||||
if (string.IsNullOrEmpty(temp)) { break; }
|
||||
path = temp;
|
||||
}
|
||||
return path == "Mods";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all xml files.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetAllContentFiles(IEnumerable<ContentPackage> contentPackages)
|
||||
{
|
||||
return contentPackages.SelectMany(f => f.Files).Select(f => f.Path).Where(p => p.EndsWith(".xml"));
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFilesOfType(IEnumerable<ContentPackage> contentPackages, ContentType type)
|
||||
{
|
||||
return contentPackages.SelectMany(f => f.Files).Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilesOfType(ContentType type)
|
||||
{
|
||||
return Files.Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public static void LoadAll(string folder)
|
||||
@@ -191,40 +424,42 @@ namespace Barotrauma
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to create directory \"" + folder + "\"", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(folder, "*.xml");
|
||||
|
||||
list.Clear();
|
||||
List.Clear();
|
||||
|
||||
foreach (string filePath in files)
|
||||
{
|
||||
ContentPackage package = new ContentPackage(filePath);
|
||||
list.Add(package);
|
||||
List.Add(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentFile
|
||||
{
|
||||
public string path;
|
||||
public ContentType type;
|
||||
public readonly string Path;
|
||||
public ContentType Type;
|
||||
|
||||
public ContentFile(string path, ContentType type)
|
||||
public Workshop.Item WorkShopItem;
|
||||
|
||||
public ContentFile(string path, ContentType type, Workshop.Item workShopItem = null)
|
||||
{
|
||||
Directory.GetCurrentDirectory();
|
||||
//Path.get
|
||||
this.path = path;
|
||||
this.type = type;
|
||||
Path = path;
|
||||
Type = type;
|
||||
WorkShopItem = workShopItem;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return path;
|
||||
return Path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user