source: TFP-WebServer/WebServer/src/UrlHandlers/ItemIconHandler.cs@ 466

Last change on this file since 466 was 463, checked in by alloc, 15 months ago

21.1.16.0 release
Completed OpenAPI specs
Add support to path handlers to register OpenAPI specs
Fixed ItemIconHandler throwing error when requested path contains no dot

File size: 6.0 KB
RevLine 
[391]1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Net;
5using UnityEngine;
6using Object = UnityEngine.Object;
7
8namespace Webserver.UrlHandlers {
9 public class ItemIconHandler : AbsHandler {
10 private readonly Dictionary<string, byte[]> icons = new Dictionary<string, byte[]> ();
11 private readonly bool logMissingFiles;
12
13 private bool loaded;
14
15 static ItemIconHandler () {
16 Instance = null;
17 }
18
19 public ItemIconHandler (bool _logMissingFiles, string _moduleName = null) : base (_moduleName) {
20 logMissingFiles = _logMissingFiles;
21 Instance = this;
22 }
23
24 public static ItemIconHandler Instance { get; private set; }
25
26 public override void HandleRequest (RequestContext _context) {
27 if (!loaded) {
28 _context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
[399]29 Log.Out ("[Web] IconHandler: Icons not loaded");
[391]30 return;
31 }
32
[463]33 if (!_context.RequestPath.EndsWith (".png", StringComparison.OrdinalIgnoreCase)) {
34 _context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
35 return;
36 }
37
[391]38 string requestFileName = _context.RequestPath.Remove (0, urlBasePath.Length);
[463]39 int indexOfExtSep = requestFileName.LastIndexOf ('.');
40 if (indexOfExtSep < 0) {
41 _context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
42 return;
43 }
[391]44
[463]45 requestFileName = requestFileName.Remove (indexOfExtSep);
[391]46
[463]47 if (!icons.TryGetValue (requestFileName, out byte[] icon)) {
48 _context.Response.StatusCode = (int)HttpStatusCode.NotFound;
[391]49 if (logMissingFiles) {
[402]50 Log.Out ($"[Web] IconHandler: FileNotFound: \"{_context.RequestPath}\" ");
[391]51 }
[463]52 return;
[391]53 }
[463]54
55 _context.Response.ContentType = MimeType.GetMimeType (".png");
56
57 _context.Response.ContentLength64 = icon.Length;
58 _context.Response.OutputStream.Write (icon, 0, icon.Length);
[391]59 }
60
[402]61 private class LoadingStats {
62 public int Files;
63 public int Tints;
64 public readonly MicroStopwatch MswTotal = new MicroStopwatch (false);
65 public readonly MicroStopwatch MswLoading = new MicroStopwatch (false);
66 public readonly MicroStopwatch MswEncoding = new MicroStopwatch (false);
67 public readonly MicroStopwatch MswTinting = new MicroStopwatch (false);
68 }
69
[391]70 public bool LoadIcons () {
71
72 lock (icons) {
73 if (loaded) {
74 return true;
75 }
76
[402]77 LoadingStats stats = new LoadingStats ();
78 stats?.MswTotal.Start ();
[391]79
80 // Get list of used tints for all items
81 Dictionary<string, List<Color>> tintedIcons = new Dictionary<string, List<Color>> ();
82 foreach (ItemClass ic in ItemClass.list) {
83 if (ic == null) {
84 continue;
85 }
86
87 Color tintColor = ic.GetIconTint ();
88 if (tintColor == Color.white) {
89 continue;
90 }
91
92 string name = ic.GetIconName ();
[402]93 if (!tintedIcons.TryGetValue (name, out List<Color> tintsList)) {
94 tintsList = new List<Color> ();
95 tintedIcons.Add (name, tintsList);
[391]96 }
97
[402]98 tintsList.Add (tintColor);
[391]99 }
100
101 try {
[402]102 loadIconsFromFolder (GameIO.GetGameDir ("Data/ItemIcons"), tintedIcons, stats);
[391]103 } catch (Exception e) {
[399]104 Log.Error ("[Web] Failed loading icons from base game");
[391]105 Log.Exception (e);
106 }
107
108 // Load icons from mods
109 foreach (Mod mod in ModManager.GetLoadedMods ()) {
110 try {
[402]111 string modIconsPath = $"{mod.Path}/ItemIcons";
112 loadIconsFromFolder (modIconsPath, tintedIcons, stats);
[391]113 } catch (Exception e) {
[402]114 Log.Error ($"[Web] Failed loading icons from mod {mod.Name}");
[391]115 Log.Exception (e);
116 }
117 }
[402]118
[391]119 loaded = true;
120
[402]121 if (stats == null) {
122 Log.Out ($"[Web] IconHandler: Loaded {icons.Count} icons");
123 } else {
124 stats?.MswTotal.Stop ();
125 Log.Out ($"[Web] IconHandler: Loaded {icons.Count} icons ({stats.Files} source images with {stats.Tints} tints applied)");
126 Log.Out ($"[Web] IconHandler: Total time {stats.MswTotal.ElapsedMilliseconds} ms, loading files {stats.MswLoading.ElapsedMilliseconds} ms, tinting files {stats.MswTinting.ElapsedMilliseconds} ms, encoding files {stats.MswEncoding.ElapsedMilliseconds} ms");
127
128 int totalSize = 0;
129 foreach ((string _, byte[] iconData) in icons) {
130 totalSize += iconData.Length;
131 }
132
133 Log.Out ($"[Web] IconHandler: Cached {totalSize / 1024} KiB");
134 }
135
[391]136 return true;
137 }
138 }
139
[402]140 private void loadIconsFromFolder (string _path, Dictionary<string, List<Color>> _tintedIcons, LoadingStats _stats) {
[391]141 if (!Directory.Exists (_path)) {
142 return;
143 }
144
145 foreach (string file in Directory.GetFiles (_path)) {
146 try {
147 if (!file.EndsWith (".png", StringComparison.OrdinalIgnoreCase)) {
148 continue;
149 }
150
151 string name = Path.GetFileNameWithoutExtension (file);
152 Texture2D tex = new Texture2D (1, 1, TextureFormat.ARGB32, false);
[402]153
154 _stats?.MswLoading.Start ();
155 byte[] sourceBytes = File.ReadAllBytes (file);
156 if (!tex.LoadImage (sourceBytes)) {
157 _stats?.MswLoading.Stop ();
[391]158 continue;
159 }
[402]160 _stats?.MswLoading.Stop ();
[391]161
[402]162 AddIcon (name, sourceBytes, tex, _tintedIcons, _stats);
[391]163
164 Object.Destroy (tex);
165 } catch (Exception e) {
166 Log.Exception (e);
167 }
168 }
169 }
170
[402]171 private void AddIcon (string _name, byte[] _sourceBytes, Texture2D _tex, Dictionary<string, List<Color>> _tintedIcons, LoadingStats _stats) {
172 _stats?.MswEncoding.Start ();
173 icons [$"{_name}__FFFFFF"] = _sourceBytes;
174 _stats?.MswEncoding.Stop ();
[391]175
[402]176 if (_stats != null) {
177 _stats.Files++;
178 }
179
180 if (!_tintedIcons.TryGetValue (_name, out List<Color> tintsList)) {
[391]181 return;
182 }
183
[402]184 foreach (Color c in tintsList) {
185 string tintedName = $"{_name}__{c.ToHexCode ()}";
[391]186 if (icons.ContainsKey (tintedName)) {
187 continue;
188 }
189
190 Texture2D tintedTex = new Texture2D (_tex.width, _tex.height, TextureFormat.ARGB32, false);
191
[402]192 _stats?.MswTinting.Start ();
193 TextureUtils.ApplyTint (_tex, tintedTex, c);
194 _stats?.MswTinting.Stop ();
[391]195
[402]196 _stats?.MswEncoding.Start ();
[391]197 icons [tintedName] = tintedTex.EncodeToPNG ();
[402]198 _stats?.MswEncoding.Stop ();
[391]199
200 Object.Destroy (tintedTex);
[402]201
202 if (_stats != null) {
203 _stats.Tints++;
204 }
[391]205 }
206 }
[402]207
[391]208 }
209}
Note: See TracBrowser for help on using the repository browser.