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

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

fixes

File size: 10.7 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 MapRenderBlockBuffer[] zoomLevelBuffers;
25                private Dictionary<Vector2i, Color[]> dirtyChunks = new Dictionary<Vector2i, Color[]> ();
26                private System.Timers.Timer chunkSaveTimer = new System.Timers.Timer (500);
27                private bool renderingFullMap = false;
28                public static bool renderingEnabled = true;
29                private MicroStopwatch msw = new MicroStopwatch ();
30
31                private MapRendering ()
32                {
33                        Constants.MAP_DIRECTORY = StaticDirectories.GetSaveGameDir () + "/map";
34
35                        if (File.Exists (Constants.MAP_DIRECTORY + "/mapinfo.json")) {
36                                LoadMapInfo ();
37                        } else {
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 (Instance.zoomLevelBuffers) {
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 (Instance.zoomLevelBuffers) {
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 (zoomLevelBuffers) {
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 void LoadMapInfo ()
254                {
255                        if (File.Exists (Constants.MAP_DIRECTORY + "/mapinfo.json")) {
256                                string json = File.ReadAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", Encoding.UTF8);
257                                JSONNode node = Parser.Parse (json);
258                                if (node is JSONObject) {
259                                        JSONObject jo = (JSONObject)node;
260                                        if (jo.ContainsKey ("blockSize"))
261                                                Constants.MAP_BLOCK_SIZE = ((JSONNumber)jo ["blockSize"]).GetInt ();
262                                        if (jo.ContainsKey ("maxZoom"))
263                                                Constants.ZOOMLEVELS = ((JSONNumber)jo ["maxZoom"]).GetInt () + 1;
264                                }
265                        }
266                }
267
268                private void getWorldExtent (RegionFileManager rfm, out Vector2i minChunk, out Vector2i maxChunk,
269                                            out Vector2i minPos, out Vector2i maxPos,
270                                            out int widthChunks, out int heightChunks,
271                                            out int widthPix, out int heightPix)
272                {
273                        minChunk = default(Vector2i);
274                        maxChunk = default(Vector2i);
275                        minPos = default(Vector2i);
276                        maxPos = default(Vector2i);
277
278                        long[] keys = rfm.GetAllChunkKeys ();
279                        int minX = Int32.MaxValue;
280                        int minY = Int32.MaxValue;
281                        int maxX = Int32.MinValue;
282                        int maxY = Int32.MinValue;
283                        foreach (long key in keys) {
284                                int x = WorldChunkCache.extractX (key);
285                                int y = WorldChunkCache.extractZ (key);
286
287                                if (x < minX)
288                                        minX = x;
289                                if (x > maxX)
290                                        maxX = x;
291                                if (y < minY)
292                                        minY = y;
293                                if (y > maxY)
294                                        maxY = y;
295                        }
296
297                        minChunk.x = minX;
298                        minChunk.y = minY;
299
300                        maxChunk.x = maxX;
301                        maxChunk.y = maxY;
302
303                        minPos.x = minX * Constants.MAP_CHUNK_SIZE;
304                        minPos.y = minY * Constants.MAP_CHUNK_SIZE;
305
306                        maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
307                        maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
308
309                        widthChunks = maxX - minX + 1;
310                        heightChunks = maxY - minY + 1;
311
312                        widthPix = widthChunks * Constants.MAP_CHUNK_SIZE;
313                        heightPix = heightChunks * Constants.MAP_CHUNK_SIZE;
314                }
315
316                private static Color shortColorToColor (ushort col)
317                {
318                        return new Color (((float)(col >> 10 & 31) / 31f), ((float)(col >> 5 & 31) / 31f), ((float)(col & 31) / 31f), 255);
319                }
320
321        }
322}
Note: See TracBrowser for help on using the repository browser.