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

Last change on this file since 463 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
Line 
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;
29 Log.Out ("[Web] IconHandler: Icons not loaded");
30 return;
31 }
32
33 if (!_context.RequestPath.EndsWith (".png", StringComparison.OrdinalIgnoreCase)) {
34 _context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
35 return;
36 }
37
38 string requestFileName = _context.RequestPath.Remove (0, urlBasePath.Length);
39 int indexOfExtSep = requestFileName.LastIndexOf ('.');
40 if (indexOfExtSep < 0) {
41 _context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
42 return;
43 }
44
45 requestFileName = requestFileName.Remove (indexOfExtSep);
46
47 if (!icons.TryGetValue (requestFileName, out byte[] icon)) {
48 _context.Response.StatusCode = (int)HttpStatusCode.NotFound;
49 if (logMissingFiles) {
50 Log.Out ($"[Web] IconHandler: FileNotFound: \"{_context.RequestPath}\" ");
51 }
52 return;
53 }
54
55 _context.Response.ContentType = MimeType.GetMimeType (".png");
56
57 _context.Response.ContentLength64 = icon.Length;
58 _context.Response.OutputStream.Write (icon, 0, icon.Length);
59 }
60
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
70 public bool LoadIcons () {
71
72 lock (icons) {
73 if (loaded) {
74 return true;
75 }
76
77 LoadingStats stats = new LoadingStats ();
78 stats?.MswTotal.Start ();
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 ();
93 if (!tintedIcons.TryGetValue (name, out List<Color> tintsList)) {
94 tintsList = new List<Color> ();
95 tintedIcons.Add (name, tintsList);
96 }
97
98 tintsList.Add (tintColor);
99 }
100
101 try {
102 loadIconsFromFolder (GameIO.GetGameDir ("Data/ItemIcons"), tintedIcons, stats);
103 } catch (Exception e) {
104 Log.Error ("[Web] Failed loading icons from base game");
105 Log.Exception (e);
106 }
107
108 // Load icons from mods
109 foreach (Mod mod in ModManager.GetLoadedMods ()) {
110 try {
111 string modIconsPath = $"{mod.Path}/ItemIcons";
112 loadIconsFromFolder (modIconsPath, tintedIcons, stats);
113 } catch (Exception e) {
114 Log.Error ($"[Web] Failed loading icons from mod {mod.Name}");
115 Log.Exception (e);
116 }
117 }
118
119 loaded = true;
120
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
136 return true;
137 }
138 }
139
140 private void loadIconsFromFolder (string _path, Dictionary<string, List<Color>> _tintedIcons, LoadingStats _stats) {
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);
153
154 _stats?.MswLoading.Start ();
155 byte[] sourceBytes = File.ReadAllBytes (file);
156 if (!tex.LoadImage (sourceBytes)) {
157 _stats?.MswLoading.Stop ();
158 continue;
159 }
160 _stats?.MswLoading.Stop ();
161
162 AddIcon (name, sourceBytes, tex, _tintedIcons, _stats);
163
164 Object.Destroy (tex);
165 } catch (Exception e) {
166 Log.Exception (e);
167 }
168 }
169 }
170
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 ();
175
176 if (_stats != null) {
177 _stats.Files++;
178 }
179
180 if (!_tintedIcons.TryGetValue (_name, out List<Color> tintsList)) {
181 return;
182 }
183
184 foreach (Color c in tintsList) {
185 string tintedName = $"{_name}__{c.ToHexCode ()}";
186 if (icons.ContainsKey (tintedName)) {
187 continue;
188 }
189
190 Texture2D tintedTex = new Texture2D (_tex.width, _tex.height, TextureFormat.ARGB32, false);
191
192 _stats?.MswTinting.Start ();
193 TextureUtils.ApplyTint (_tex, tintedTex, c);
194 _stats?.MswTinting.Stop ();
195
196 _stats?.MswEncoding.Start ();
197 icons [tintedName] = tintedTex.EncodeToPNG ();
198 _stats?.MswEncoding.Stop ();
199
200 Object.Destroy (tintedTex);
201
202 if (_stats != null) {
203 _stats.Tints++;
204 }
205 }
206 }
207
208 }
209}
Note: See TracBrowser for help on using the repository browser.