diff --git a/TrueCraft.Client/Rendering/TextureMapper.cs b/TrueCraft.Client/Rendering/TextureMapper.cs new file mode 100644 index 0000000..aac19de --- /dev/null +++ b/TrueCraft.Client/Rendering/TextureMapper.cs @@ -0,0 +1,152 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using TrueCraft.Core; + +namespace TrueCraft.Client.Rendering +{ + /// + /// Provides mappings from keys to textures. + /// + public sealed class TextureMapper : IDisposable + { + /// + /// + /// + public static readonly IDictionary Defaults = + new Dictionary(); + + /// + /// + /// + /// + public static void LoadDefaults(GraphicsDevice graphicsDevice) + { + Defaults.Clear(); + + Defaults.Add("items.png", Texture2D.FromStream(graphicsDevice, File.OpenRead("Content/items.png"))); + Defaults.Add("terrain.png", Texture2D.FromStream(graphicsDevice, File.OpenRead("Content/terrain.png"))); + } + + /// + /// + /// + public TexturePack TexturePack { get; private set; } + + /// + /// + /// + private IDictionary Customs { get; set; } + + /// + /// + /// + public bool IsDisposed { get; private set; } + + /// + /// + /// + /// + /// + public TextureMapper(GraphicsDevice graphicsDevice, TexturePack texturePack = null) + { + if (graphicsDevice == null) + throw new ArgumentException(); + + TexturePack = texturePack; + Customs = new Dictionary(); + IsDisposed = false; + + if (TexturePack != null) + LoadTextures(graphicsDevice); + } + + /// + /// + /// + /// + private void LoadTextures(GraphicsDevice graphicsDevice) + { + foreach (var entry in TexturePack.Archive.Entries) + { + // Make sure to 'silence' errors loading custom texture packs; + // they're unimportant as we can just use default textures. + try + { + var key = entry.FileName; + using (var stream = entry.OpenReader()) + Customs.Add(key, Texture2D.FromStream(graphicsDevice, stream)); + } + catch { } + } + } + + /// + /// + /// + /// + /// + public Texture2D GetTexture(string key) + { + Texture2D result = null; + TryGetTexture(key, out result); + return result; + } + + /// + /// + /// + /// + /// + /// + public bool TryGetTexture(string key, out Texture2D texture) + { + // -> Try to load from external texture pack + // -> Try to load from default texture pack + // -> Fail gracefully + + if (string.IsNullOrEmpty(key)) + throw new ArgumentException(); + + bool hasTexture = false; + texture = null; + if (TexturePack != null) + { + Texture2D customTexture = null; + var inCustom = Customs.TryGetValue(key, out customTexture); + texture = (inCustom) ? customTexture : null; + hasTexture = inCustom; + } + + if (!hasTexture) + { + Texture2D defaultTexture = null; + var inDefault = TextureMapper.Defaults.TryGetValue(key, out defaultTexture); + texture = (inDefault) ? defaultTexture : null; + hasTexture = inDefault; + } + + return hasTexture; + } + + /// + /// + /// + public void Dispose() + { + if (IsDisposed) + return; + + foreach (var pair in Customs) + pair.Value.Dispose(); + + Customs.Clear(); + Customs = null; + TexturePack = null; + IsDisposed = true; + } + } +} diff --git a/TrueCraft.Client/TrueCraft.Client.csproj b/TrueCraft.Client/TrueCraft.Client.csproj index 3a88eea..a01f728 100644 --- a/TrueCraft.Client/TrueCraft.Client.csproj +++ b/TrueCraft.Client/TrueCraft.Client.csproj @@ -114,6 +114,7 @@ + diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index 35143f5..ad60449 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -43,6 +43,7 @@ namespace TrueCraft.Client private GameTime GameTime { get; set; } private Microsoft.Xna.Framework.Vector3 Delta { get; set; } private TexturePack TexturePack { get; set; } + private TextureMapper TextureMapper { get; set; } private BasicEffect OpaqueEffect, TransparentEffect; @@ -126,7 +127,14 @@ namespace TrueCraft.Client protected override void LoadContent() { - TexturePack = new TexturePack(UserSettings.Local.SelectedTexturePack); + // Ensure we have default textures loaded. + TextureMapper.LoadDefaults(GraphicsDevice); + + // Load any custom textures if needed. + TexturePack = (UserSettings.Local.SelectedTexturePack != TexturePack.DefaultID) ? + new TexturePack(UserSettings.Local.SelectedTexturePack) : null; + TextureMapper = new TextureMapper(GraphicsDevice, TexturePack); + DejaVu = new FontRenderer( new Font(Content, "Fonts/DejaVu", FontStyle.Regular), new Font(Content, "Fonts/DejaVu", FontStyle.Bold), @@ -141,7 +149,7 @@ namespace TrueCraft.Client OpaqueEffect.DirectionalLight1.SpecularColor = Color.Black.ToVector3(); OpaqueEffect.DirectionalLight2.SpecularColor = Color.Black.ToVector3(); OpaqueEffect.TextureEnabled = true; - OpaqueEffect.Texture = TexturePack.GetTexture(GraphicsDevice, "terrain.png"); + OpaqueEffect.Texture = TextureMapper.GetTexture("terrain.png"); OpaqueEffect.FogEnabled = true; OpaqueEffect.FogStart = 512f; OpaqueEffect.FogEnd = 1000f; @@ -149,7 +157,7 @@ namespace TrueCraft.Client TransparentEffect = new BasicEffect(GraphicsDevice); TransparentEffect.TextureEnabled = true; - TransparentEffect.Texture = TexturePack.GetTexture(GraphicsDevice, "terrain.png"); + TransparentEffect.Texture = TextureMapper.GetTexture("terrain.png"); base.LoadContent(); } diff --git a/TrueCraft.Core/TexturePack.cs b/TrueCraft.Core/TexturePack.cs index fc5c27d..b0da3f7 100644 --- a/TrueCraft.Core/TexturePack.cs +++ b/TrueCraft.Core/TexturePack.cs @@ -12,12 +12,7 @@ namespace TrueCraft.Core /// /// /// - public const string DescriptionFile = "pack.txt"; - - /// - /// - /// - public const string ImageFile = "pack.png"; + public const string DefaultID = "#Default"; /// /// @@ -56,6 +51,16 @@ namespace TrueCraft.Core /// public MemoryStream Image { get; private set; } + /// + /// + /// + public TexturePack() + { + Path = TexturePack.DefaultID; + Name = "Default"; + Archive = new ZipFile(); + } + /// /// /// @@ -65,7 +70,6 @@ namespace TrueCraft.Core if (string.IsNullOrEmpty(path) || !File.Exists(path)) throw new ArgumentException(); - Path = path; var fileInfo = new FileInfo(path); // A bit weird, but it works. Name = fileInfo.Name; @@ -82,7 +86,7 @@ namespace TrueCraft.Core { foreach (var entry in Archive.Entries) { - if (entry.FileName == TexturePack.DescriptionFile) + if (entry.FileName == "pack.txt") { using (var stream = entry.OpenReader()) { @@ -90,7 +94,7 @@ namespace TrueCraft.Core Description = reader.ReadToEnd(); } } - else if (entry.FileName == TexturePack.ImageFile) + else if (entry.FileName == "pack.png") { using (var stream = entry.OpenReader()) { @@ -99,6 +103,9 @@ namespace TrueCraft.Core stream.Read(buffer, 0, buffer.Length); Image = new MemoryStream((int)entry.UncompressedSize); Image.Write(buffer, 0, buffer.Length); + + // Fixes 'GLib.GException: Unrecognized image file format' on Linux. + Image.Seek(0, SeekOrigin.Begin); } } } diff --git a/TrueCraft.Core/UserSettings.cs b/TrueCraft.Core/UserSettings.cs index 63a5364..86a9543 100644 --- a/TrueCraft.Core/UserSettings.cs +++ b/TrueCraft.Core/UserSettings.cs @@ -30,7 +30,7 @@ namespace TrueCraft.Core Username = ""; Password = ""; LastIP = ""; - SelectedTexturePack = "Default.zip"; + SelectedTexturePack = TexturePack.DefaultID; FavoriteServers = new FavoriteServer[0]; } diff --git a/TrueCraft.Launcher/Content/Default.zip b/TrueCraft.Launcher/Content/Default.zip deleted file mode 100644 index 2fe999f..0000000 Binary files a/TrueCraft.Launcher/Content/Default.zip and /dev/null differ diff --git a/TrueCraft.Launcher/Content/default-pack.png b/TrueCraft.Launcher/Content/default-pack.png new file mode 100644 index 0000000..cf893db Binary files /dev/null and b/TrueCraft.Launcher/Content/default-pack.png differ diff --git a/TrueCraft.Launcher/Content/default-pack.txt b/TrueCraft.Launcher/Content/default-pack.txt new file mode 100644 index 0000000..b90cfbc --- /dev/null +++ b/TrueCraft.Launcher/Content/default-pack.txt @@ -0,0 +1 @@ +No description available. \ No newline at end of file diff --git a/TrueCraft.Launcher/TrueCraft.Launcher.csproj b/TrueCraft.Launcher/TrueCraft.Launcher.csproj index bcf50df..b7fb5cc 100644 --- a/TrueCraft.Launcher/TrueCraft.Launcher.csproj +++ b/TrueCraft.Launcher/TrueCraft.Launcher.csproj @@ -57,6 +57,12 @@ Xwt.Gtk.dll PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -85,9 +91,6 @@ - - PreserveNewest - PreserveNewest diff --git a/TrueCraft.Launcher/Views/OptionView.cs b/TrueCraft.Launcher/Views/OptionView.cs index cdc42a2..cf2e9b9 100644 --- a/TrueCraft.Launcher/Views/OptionView.cs +++ b/TrueCraft.Launcher/Views/OptionView.cs @@ -11,6 +11,8 @@ namespace TrueCraft.Launcher.Views public class OptionView : VBox { public LauncherWindow Window { get; set; } + public Image DefaultImage { get; set; } + public string DefaultDescription { get; set; } public Label OptionLabel { get; set; } public Label TexturePackLabel { get; set; } @@ -27,6 +29,9 @@ namespace TrueCraft.Launcher.Views public OptionView(LauncherWindow window) { + DefaultImage = Image.FromFile("Content/default-pack.png"); + DefaultDescription = File.ReadAllText("Content/default-pack.txt"); + _texturePacks = new List(); _lastTexturePack = null; @@ -89,8 +94,8 @@ namespace TrueCraft.Launcher.Views private void LoadTexturePacks() { - // We load Default.zip from a different directory. - var defaultPack = new TexturePack("Content/Default.zip"); + // We load the default texture pack specially. + var defaultPack = new TexturePack(); _texturePacks.Add(defaultPack); AddTexturePackRow(defaultPack); @@ -113,9 +118,9 @@ namespace TrueCraft.Launcher.Views private void AddTexturePackRow(TexturePack pack) { var row = TexturePackStore.AddRow(); - TexturePackStore.SetValue(row, TexturePackImageField, Image.FromStream(pack.Image).WithSize(IconSize.Medium)); + TexturePackStore.SetValue(row, TexturePackImageField, (pack.Image == null) ? DefaultImage.WithSize(IconSize.Medium) : Image.FromStream(pack.Image).WithSize(IconSize.Medium)); TexturePackStore.SetValue(row, TexturePackNameField, pack.Name); - TexturePackStore.SetValue(row, TexturePackDescField, pack.Description); + TexturePackStore.SetValue(row, TexturePackDescField, pack.Description ?? DefaultDescription); } } }