source: binary-improvements/7dtd-server-fixes/src/MapRendering/MapRendering.cs @ 192

Last change on this file since 192 was 192, checked in by alloc, 6 years ago

fixes

File size: 10.9 KB
Line 
1using AllocsFixes.JSON;
2using System;
3using System.Collections.Generic;
4using System.IO;
5using System.Text;
6using System.Threading;
7using UnityEngine;
8
9namespace AllocsFixes.MapRendering
10{
11        public class MapRendering
12        {
13                private static MapRendering instance;
14
15                public static MapRendering Instance {
16                        get {
17                                if (instance == null) {
18                                        instance = new MapRendering ();
19                                }
20                                return instance;
21                        }
22                }
23
24                private static object lockObject = new object ();
25                private MapRenderBlockBuffer[] zoomLevelBuffers;
26                private Dictionary<Vector2i, Color[]> dirtyChunks = new Dictionary<Vector2i, Color[]> ();
27                private System.Timers.Timer chunkSaveTimer = new System.Timers.Timer (500);
28                private bool renderingFullMap = false;
29                public static bool renderingEnabled = true;
30                private MicroStopwatch msw = new MicroStopwatch ();
31
32                private MapRendering ()
33                {
34                        Constants.MAP_DIRECTORY = StaticDirectories.GetSaveGameDir () + "/map";
35
36                        lock (lockObject) {
37                                if (!LoadMapInfo ())
38                                        WriteMapInfo ();
39                        }
40
41                        zoomLevelBuffers = new MapRenderBlockBuffer[Constants.ZOOMLEVELS];
42                        for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
43                                zoomLevelBuffers [i] = new MapRenderBlockBuffer (i);
44                        }
45
46                        chunkSaveTimer.AutoReset = false;
47                        chunkSaveTimer.Elapsed += new System.Timers.ElapsedEventHandler (TimedRendering);
48                }
49
50                public static void RenderSingleChunk (Chunk chunk)
51                {
52                        if (renderingEnabled) {
53                                ThreadPool.QueueUserWorkItem ((o) =>
54                                {
55                                        try {
56                                                if (!Instance.renderingFullMap) {
57                                                        lock (lockObject) {
58                                                                Chunk c = (Chunk)o;
59                                                                Vector3i cPos = c.GetWorldPos ();
60                                                                Vector2i cPos2 = new Vector2i (cPos.x / Constants.MAP_CHUNK_SIZE, cPos.z / Constants.MAP_CHUNK_SIZE);
61
62                                                                ushort[] mapColors = c.GetMapColors ();
63                                                                if (mapColors != null) {
64                                                                        Color[] realColors = new Color[Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE];
65                                                                        for (int i_colors = 0; i_colors < mapColors.Length; i_colors++) {
66                                                                                realColors [i_colors] = shortColorToColor (mapColors [i_colors]);
67                                                                        }
68                                                                        Instance.dirtyChunks [cPos2] = realColors;
69                                                                        //Log.Out ("Add Dirty: " + cPos2);
70                                                                        Instance.chunkSaveTimer.Stop ();
71                                                                        Instance.chunkSaveTimer.Start ();
72                                                                }
73                                                        }
74                                                }
75                                        } catch (Exception e) {
76                                                Log.Out ("Exception in MapRendering.RenderSingleChunk(): " + e);
77                                        }
78                                }, chunk);
79                        }
80                }
81
82                public void RenderFullMap ()
83                {
84                        MicroStopwatch microStopwatch = new MicroStopwatch ();
85
86                        string regionSaveDir = StaticDirectories.GetSaveGameRegionDir ();
87                        RegionFileManager rfm = new RegionFileManager (regionSaveDir, regionSaveDir, 0, false);
88                        Texture2D fullMapTexture = null;
89
90                        Vector2i minChunk = default(Vector2i), maxChunk = default(Vector2i);
91                        Vector2i minPos = default(Vector2i), maxPos = default(Vector2i);
92                        int widthChunks, heightChunks, widthPix, heightPix;
93                        getWorldExtent (rfm, out minChunk, out maxChunk, out minPos, out maxPos, out widthChunks, out heightChunks, out widthPix, out heightPix);
94
95                        Log.Out (String.Format ("RenderMap: min: {0}, max: {1}, minPos: {2}, maxPos: {3}, w/h: {4}/{5}, wP/hP: {6}/{7}",
96                                                minChunk.ToString (), maxChunk.ToString (),
97                                                minPos.ToString (), maxPos.ToString (),
98                                                widthChunks, heightChunks,
99                                                widthPix, heightPix)
100                        );
101
102                        lock (lockObject) {
103                                for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
104                                        zoomLevelBuffers [i].ResetBlock ();
105                                }
106
107                                if (Directory.Exists (Constants.MAP_DIRECTORY)) {
108                                        Directory.Delete (Constants.MAP_DIRECTORY, true);
109                                }
110                                WriteMapInfo ();
111
112                                renderingFullMap = true;
113
114                                if (widthPix <= 8000 && heightPix <= 8000)
115                                        fullMapTexture = new Texture2D (widthPix, heightPix);
116
117                                Vector2i curFullMapPos = default(Vector2i);
118                                Vector2i curChunkPos = default(Vector2i);
119                                for (curFullMapPos.x = 0; curFullMapPos.x < widthPix; curFullMapPos.x += Constants.MAP_CHUNK_SIZE) {
120                                        for (curFullMapPos.y = 0; curFullMapPos.y < heightPix; curFullMapPos.y += Constants.MAP_CHUNK_SIZE) {
121                                                curChunkPos.x = (curFullMapPos.x / Constants.MAP_CHUNK_SIZE) + minChunk.x;
122                                                curChunkPos.y = (curFullMapPos.y / Constants.MAP_CHUNK_SIZE) + minChunk.y;
123
124                                                try {
125                                                        long chunkKey = WorldChunkCache.MakeChunkKey (curChunkPos.x, curChunkPos.y);
126                                                        if (rfm.ContainsChunkSync (chunkKey)) {
127                                                                Chunk c = rfm.GetChunkSync (chunkKey);
128                                                                ushort[] mapColors = c.GetMapColors ();
129                                                                if (mapColors != null) {
130                                                                        Color[] realColors = new Color[Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE];
131                                                                        for (int i_colors = 0; i_colors < mapColors.Length; i_colors++) {
132                                                                                realColors [i_colors] = shortColorToColor (mapColors [i_colors]);
133                                                                        }
134                                                                        dirtyChunks [curChunkPos] = realColors;
135                                                                        if (fullMapTexture != null)
136                                                                                fullMapTexture.SetPixels (curFullMapPos.x, curFullMapPos.y, Constants.MAP_CHUNK_SIZE, Constants.MAP_CHUNK_SIZE, realColors);
137                                                                }
138                                                        }
139                                                } catch (Exception e) {
140                                                        Log.Out ("Exception: " + e);
141                                                }
142                                        }
143
144                                        while (dirtyChunks.Count > 0) {
145                                                RenderDirtyChunks ();
146                                        }
147
148                                        Log.Out (String.Format ("RenderMap: {0}/{1} ({2}%)", curFullMapPos.x, widthPix, (int)((float)curFullMapPos.x / widthPix * 100)));
149                                }
150                        }
151
152                        if (fullMapTexture != null) {
153                                byte[] array = fullMapTexture.EncodeToPNG ();
154                                File.WriteAllBytes (Constants.MAP_DIRECTORY + "/map.png", array);
155                                Texture2D.Destroy (fullMapTexture);
156                                fullMapTexture = null;
157                        }
158
159                        renderingFullMap = false;
160
161                        Log.Out ("Generating map took: " + microStopwatch.ElapsedMilliseconds + " ms");
162                        Log.Out ("World extent: " + minPos + " - " + maxPos);
163                }
164
165                private void SaveAllBlockMaps (object source, System.Timers.ElapsedEventArgs e)
166                {
167                        for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
168                                zoomLevelBuffers [i].SaveBlock ();
169                        }
170                }
171
172                private void TimedRendering (object source, System.Timers.ElapsedEventArgs e)
173                {
174                        lock (lockObject) {
175                                RenderDirtyChunks ();
176                                if (dirtyChunks.Count > 0)
177                                        Instance.chunkSaveTimer.Start ();
178                        }
179                }
180
181                private void RenderDirtyChunks ()
182                {
183                        msw.ResetAndRestart ();
184
185                        if (dirtyChunks.Count > 0) {
186                                List<Vector2i> keys = new List<Vector2i> (dirtyChunks.Keys);
187                                List<Vector2i> chunksDone = new List<Vector2i> ();
188
189                                Vector2i chunkPos = keys [0];
190                                chunksDone.Add (chunkPos);
191
192                                //Log.Out ("Start Dirty: " + chunkPos);
193
194                                Vector2i block = default(Vector2i), blockOffset = default(Vector2i);
195                                getBlockNumber (chunkPos, out block, out blockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV, Constants.MAP_CHUNK_SIZE);
196
197                                zoomLevelBuffers [Constants.ZOOMLEVELS - 1].LoadBlock (block);
198
199                                Vector2i v_block = default(Vector2i), v_blockOffset = default(Vector2i);
200                                foreach (Vector2i v in keys) {
201                                        getBlockNumber (v, out v_block, out v_blockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV, Constants.MAP_CHUNK_SIZE);
202                                        if (v_block.Equals (block)) {
203                                                //Log.Out ("Dirty: " + v + " render: true");
204                                                chunksDone.Add (v);
205                                                zoomLevelBuffers [Constants.ZOOMLEVELS - 1].SetPart (v_blockOffset, Constants.MAP_CHUNK_SIZE, dirtyChunks [v]);
206                                        } else {
207                                                //Log.Out ("Dirty: " + v + " render: false");
208                                        }
209                                }
210
211                                foreach (Vector2i v in chunksDone)
212                                        dirtyChunks.Remove (v);
213
214                                RenderZoomLevel (Constants.ZOOMLEVELS - 1, block);
215
216                                SaveAllBlockMaps (null, null);
217                        }
218                }
219
220                private void RenderZoomLevel (int level, Vector2i innerBlock)
221                {
222                        if (level > 0) {
223                                Vector2i block = default(Vector2i), blockOffset = default(Vector2i);
224                                getBlockNumber (innerBlock, out block, out blockOffset, 2, Constants.MAP_BLOCK_SIZE / 2);
225
226                                zoomLevelBuffers [level - 1].LoadBlock (block);
227                                zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaled ());
228
229                                RenderZoomLevel (level - 1, block);
230                        }
231                }
232
233                private void getBlockNumber (Vector2i innerPos, out Vector2i block, out Vector2i blockOffset, int scaleFactor, int offsetSize)
234                {
235                        block = default(Vector2i);
236                        blockOffset = default(Vector2i);
237                        block.x = ((innerPos.x + 16777216) / scaleFactor) - (16777216 / scaleFactor);
238                        block.y = ((innerPos.y + 16777216) / scaleFactor) - (16777216 / scaleFactor);
239                        blockOffset.x = ((innerPos.x + 16777216) % scaleFactor) * offsetSize;
240                        blockOffset.y = ((innerPos.y + 16777216) % scaleFactor) * offsetSize;
241                }
242
243                private void WriteMapInfo ()
244                {
245                        JSONObject mapInfo = new JSONObject ();
246                        mapInfo.Add ("blockSize", new JSONNumber (Constants.MAP_BLOCK_SIZE));
247                        mapInfo.Add ("maxZoom", new JSONNumber (Constants.ZOOMLEVELS - 1));
248
249                        Directory.CreateDirectory (Constants.MAP_DIRECTORY);
250                        File.WriteAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", mapInfo.ToString (), Encoding.UTF8);
251                }
252
253                private bool LoadMapInfo ()
254                {
255                        if (File.Exists (Constants.MAP_DIRECTORY + "/mapinfo.json")) {
256                                string json = File.ReadAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", Encoding.UTF8);
257                                try {
258                                        JSONNode node = Parser.Parse (json);
259                                        if (node is JSONObject) {
260                                                JSONObject jo = (JSONObject)node;
261                                                if (jo.ContainsKey ("blockSize"))
262                                                        Constants.MAP_BLOCK_SIZE = ((JSONNumber)jo ["blockSize"]).GetInt ();
263                                                if (jo.ContainsKey ("maxZoom"))
264                                                        Constants.ZOOMLEVELS = ((JSONNumber)jo ["maxZoom"]).GetInt () + 1;
265                                                return true;
266                                        }
267                                } catch (MalformedJSONException e) {
268                                        Log.Out ("Exception in LoadMapInfo: " + e);
269                                } catch (InvalidCastException e) {
270                                        Log.Out ("Exception in LoadMapInfo: " + e);
271                                }
272                        }
273                        return false;
274                }
275
276                private void getWorldExtent (RegionFileManager rfm, out Vector2i minChunk, out Vector2i maxChunk,
277                                            out Vector2i minPos, out Vector2i maxPos,
278                                            out int widthChunks, out int heightChunks,
279                                            out int widthPix, out int heightPix)
280                {
281                        minChunk = default(Vector2i);
282                        maxChunk = default(Vector2i);
283                        minPos = default(Vector2i);
284                        maxPos = default(Vector2i);
285
286                        long[] keys = rfm.GetAllChunkKeys ();
287                        int minX = Int32.MaxValue;
288                        int minY = Int32.MaxValue;
289                        int maxX = Int32.MinValue;
290                        int maxY = Int32.MinValue;
291                        foreach (long key in keys) {
292                                int x = WorldChunkCache.extractX (key);
293                                int y = WorldChunkCache.extractZ (key);
294
295                                if (x < minX)
296                                        minX = x;
297                                if (x > maxX)
298                                        maxX = x;
299                                if (y < minY)
300                                        minY = y;
301                                if (y > maxY)
302                                        maxY = y;
303                        }
304
305                        minChunk.x = minX;
306                        minChunk.y = minY;
307
308                        maxChunk.x = maxX;
309                        maxChunk.y = maxY;
310
311                        minPos.x = minX * Constants.MAP_CHUNK_SIZE;
312                        minPos.y = minY * Constants.MAP_CHUNK_SIZE;
313
314                        maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
315                        maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
316
317                        widthChunks = maxX - minX + 1;
318                        heightChunks = maxY - minY + 1;
319
320                        widthPix = widthChunks * Constants.MAP_CHUNK_SIZE;
321                        heightPix = heightChunks * Constants.MAP_CHUNK_SIZE;
322                }
323
324                private static Color shortColorToColor (ushort col)
325                {
326                        return new Color (((float)(col >> 10 & 31) / 31f), ((float)(col >> 5 & 31) / 31f), ((float)(col & 31) / 31f), 255);
327                }
328
329        }
330}
Note: See TracBrowser for help on using the repository browser.