-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathSeederyIoMapGen.cs
398 lines (330 loc) · 16.8 KB
/
SeederyIoMapGen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Plugins;
using Rust;
using UnityEngine;
struct MapMonument
{
public int X;
public int Y;
public string Name;
};
namespace Oxide.Plugins
{
[Info("SeederyIoMapGen", "Pauli 'Dids' Jokela", "0.8.0")]
[Description("A public Oxide plugin for seedery.io's Rust map generation.")]
public class SeederyIoMapGen : CovalencePlugin
{
#region Properties
const string UploadUrl = "http://localhost:5000/upload";
private int _mapWidth;
private int _mapHeight;
private Terrain _terrain;
private TerrainData _terrainData;
private Vector3 _terrainPos;
#endregion
#region Plugin methods
private void OnServerInitialized()
{
GenerateMap();
}
#endregion
#region Map generation
private void GenerateMap()
{
// Initialize our global values
Initialize();
// Generate the texture
Texture2D terrainTexture = GenerateTerrainTexture(_mapWidth, _mapHeight);
// Get monuments
List<MapMonument> monuments = GetMonuments();
string monumentsJson = JsonConvert.SerializeObject(monuments, Formatting.None);
// Get server seed
int serverSeed = ConVar.Server.seed;
// Get server map size
int serverMapSize = (int)_terrain.terrainData.size.x;
// Get server protocol
string serverProtocol = Rust.Protocol.printable;
// Save it
//SaveImage(heightmapTexture, monumentsJson, serverSeed, serverMapSize, serverProtocol);
// Upload it
UploadImage(terrainTexture, monumentsJson, serverSeed, serverMapSize, serverProtocol);
// Destroy the texture
UnityEngine.Object.Destroy(terrainTexture);
}
private void Initialize()
{
// Fetch the terrain and the width and height
_terrain = TerrainMeta.Terrain;
_terrainData = TerrainMeta.Terrain.terrainData;
_terrainPos = TerrainMeta.Terrain.GetPosition();
_mapWidth = _terrainData.heightmapWidth - 1;
_mapHeight = _terrainData.heightmapHeight - 1;
}
private Texture2D GenerateTerrainTexture(int width, int height)
{
Log($"Generating a terrain texture of size: {width}x{height}");
float lowestTerrainheight = GetLowestTerrainHeight();
float highestTerrainHeight = GetHighestTerrainHeight();
Vector3 terrainStart = TerrainMeta.Terrain.GetPosition();
Vector3 terrainSize = TerrainMeta.Size;
Vector3 terrainScale = new Vector3(TerrainMeta.Size.x / width, TerrainMeta.Size.y / width, TerrainMeta.Size.z / width);
// Generate a heightmap texture
TerrainMeta.HeightMap.GenerateTextures();
Texture2D heightmapTexture = TerrainMeta.HeightMap.HeightTexture;
Texture2D heightmapNormalTexture = TerrainMeta.HeightMap.NormalTexture;
/*Texture2D heightmapTexture = new Texture2D(width, height);
for (int y = 0; y < heightmapTexture.height; y++)
{
for (int x = 0; x < heightmapTexture.width; x++)
{
float terrainHeight = _terrain.terrainData.GetHeight(x, y) - lowestTerrainheight;
float currentHeight = terrainHeight / (highestTerrainHeight - lowestTerrainheight);
float alpha = currentHeight;
//if (alpha > 0.25f) alpha -= 0.25f;
UnityEngine.Color pixelColor = new UnityEngine.Color(currentHeight, currentHeight, currentHeight, 1);
heightmapTexture.SetPixel(x, y, pixelColor);
}
}
heightmapTexture.Apply(true);*/
// Generate a biome texture
TerrainMeta.BiomeMap.GenerateTextures();
Texture2D biomemapTexture = TerrainMeta.BiomeMap.BiomeTexture;
// NOTE: Alpha map seems to contain a black texture, with a white spot where monuments are
// Generate an alphamap texture
TerrainMeta.AlphaMap.GenerateTextures();
Texture2D alphamapTexture = TerrainMeta.AlphaMap.AlphaTexture;
// Generate the final texture
Texture2D finalTexture = new Texture2D(width, height);
for (int y = 0; y < finalTexture.height; y++)
{
for (int x = 0; x < finalTexture.width; x++)
{
// Mask info
/*
public const int EVERYTHING = -1;
public const int NOTHING = 0;
public const int DIRT = 1;
public const int SNOW = 2;
public const int SAND = 4;
public const int ROCK = 8;
public const int GRASS = 16;
public const int FOREST = 32;
public const int STONES = 64;
public const int GRAVEL = 128;
public const int DIRT_IDX = 0;
public const int SNOW_IDX = 1;
public const int SAND_IDX = 2;
public const int ROCK_IDX = 3;
public const int GRASS_IDX = 4;
public const int FOREST_IDX = 5;
public const int STONES_IDX = 6;
public const int GRAVEL_IDX = 7;
*/
int mask = 1 | 2 | 4 | 8 | 16 | 64;
mask = -1; // Show all
// -1500 (-half of size)
float terrainStartX = TerrainMeta.Terrain.GetPosition().x;
float terrainStartY = TerrainMeta.Terrain.GetPosition().z;
// 3000
float terrainSizeX = TerrainMeta.Size.x;
float terrainSizeY = TerrainMeta.Size.z;
// > 1
float terrainScaleUpX = TerrainMeta.Size.x / width;
float terrainScaleUpY = TerrainMeta.Size.z / height;
// < 0
float terrainScaleDownX = width / TerrainMeta.Size.x;
float terrainScaleDownY = height / TerrainMeta.Size.z;
// Scale our coordinates to match with the terrain
float startX = x * terrainScaleUpX;
float startY = y * terrainScaleUpY;
// Offset our coordinates to match the terrain
float calculatedTerrainX = startX + terrainStartX;
float calculatedTerrainY = startY + terrainStartY;
// Create the position (in world coordinates) that we want to sample a color from
Vector3 terrainWorldPosition = new Vector3(calculatedTerrainX, 0, calculatedTerrainY);
// Get the water depth at the current location
float waterDepth = TerrainMeta.WaterMap.GetDepth(terrainWorldPosition) + 0.5f; // NOTE: I'm assuming there's waves, because this "floats" around 49-50, so +0.5 for accuracy
// Apply it to the texture (if above or equal to water level), note that this includes rivers!
float terrainHeight = _terrain.terrainData.GetHeight(x, y) - lowestTerrainheight;
float currentHeight = terrainHeight / (highestTerrainHeight - lowestTerrainheight);
if (currentHeight > waterDepth)
{
UnityEngine.Color pixelColor = TerrainMeta.Colors.GetColor(terrainWorldPosition, mask);
pixelColor.a = TerrainMeta.AlphaMap.GetAlpha(x, y); // This basically removes little spots all over the map
finalTexture.SetPixel(x, y, pixelColor);
}
// Otherwise just draw a dark water color
else
{
UnityEngine.Color blueColor = new UnityEngine.Color((1f / 255f) * 72f, (1f / 255f) * 61f, (1f / 255f) * 139f, 1);
finalTexture.SetPixel(x, y, blueColor);
}
// Alpha blend the biomemap with the existing texture
/*if (biomemapTexture != null)
{
var alphaBlendedColor = AlphaBlend(finalTexture.GetPixel(x, y), biomemapTexture.GetPixel(x, y));
finalTexture.SetPixel(x, y, alphaBlendedColor);
}*/
// Alpha blend the alphamap with the existing texture
if (alphamapTexture != null)
{
var alphaBlendedColor = AlphaBlend(finalTexture.GetPixel(x, y), alphamapTexture.GetPixel(x, y));
finalTexture.SetPixel(x, y, alphaBlendedColor);
}
// Alpha blend the heightmap normal with the existing texture
if (heightmapNormalTexture != null)
{
var finalTextureColor = finalTexture.GetPixel(x, y);
var heightmapNormalTextureColor = heightmapNormalTexture.GetPixel(x, y);
/*// Convert the color to HSV
float h, s, v;
Color.RGBToHSV(heightmapNormalTextureColor, out h, out s, out v);
// Modify the HSV color to be brighter
h *= 1.0f;
s *= 1.0f;
v *= 1.0f;
// Convert it back to RGB
heightmapNormalTextureColor = Color.HSVToRGB(h, s, v);*/
//var alphaBlendedColor = AlphaBlend(finalTextureColor, heightmapNormalTextureColor);
// Alpha blend the heightmap normal texture color (top) with the final texture color (bottom)
var alphaBlendedColor = AlphaBlend(heightmapNormalTextureColor, finalTextureColor);
// Assign the pixel color
finalTexture.SetPixel(x, y, alphaBlendedColor);
}
}
}
finalTexture.Apply();
return finalTexture;
}
private float GetLowestTerrainHeight()
{
float lowestHeight = TerrainMeta.Size.y;
for (var x = 0; x < TerrainMeta.Size.x; x++)
{
for (var y = 0; y < TerrainMeta.Size.z; y++)
{
var h = _terrain.terrainData.GetHeight(x, y);
if (h < lowestHeight) lowestHeight = h;
}
}
return lowestHeight;
}
private float GetHighestTerrainHeight()
{
float highestHeight = 0;
for (var x = 0; x < TerrainMeta.Size.x; x++)
{
for (var y = 0; y < TerrainMeta.Size.z; y++)
{
var h = _terrain.terrainData.GetHeight(x, y);
if (h > highestHeight) highestHeight = h;
}
}
return highestHeight;
}
private List<MapMonument> GetMonuments()
{
var results = new List<MapMonument>();
var monuments = UnityEngine.Object.FindObjectsOfType<MonumentInfo>();
Log("Found " + monuments.Length + " monuments.");
foreach (var m in monuments)
{
MapMonument monument;
monument.X = (int)ConvertWorldToMapCoordinates(m.transform.position.x, _mapWidth);
monument.Y = (int)ConvertWorldToMapCoordinates(-m.transform.position.z, _mapWidth); // Note how we're flipping the Z coordinate (Y in our case)
monument.Name = "Unknown";
if (m.name.ToLower().Contains("lighthouse")) monument.Name = "Lighthouse";
else if (m.name.ToLower().Contains("cave_small")) monument.Name = "Cave (Small)";
else if (m.name.ToLower().Contains("cave_medium")) monument.Name = "Cave (Medium)";
else if (m.name.ToLower().Contains("cave_large")) monument.Name = "Cave (Large)";
else if (m.name.ToLower().Contains("power_sub_small")) monument.Name = "Power Substation (Small)";
else if (m.name.ToLower().Contains("power_sub_big")) monument.Name = "Power Substation (Big)";
else if (m.name.ToLower().Contains("warehouse")) monument.Name = "Warehouse";
else if (m.name.ToLower().Contains("sphere_tank")) monument.Name = "Sphere";
else if (m.name.ToLower().Contains("satellite")) monument.Name = "Satellite";
else if (m.name.ToLower().Contains("radtown_small")) monument.Name = "Radtown";
else if (m.name.ToLower().Contains("powerplant")) monument.Name = "Powerplant";
else if (m.name.ToLower().Contains("trainyard")) monument.Name = "Trainyard";
else if (m.name.ToLower().Contains("airfield")) monument.Name = "Airfield";
else if (m.name.ToLower().Contains("military_tunnel")) monument.Name = "Military Tunnel";
else if (m.name.ToLower().Contains("water_treatment_plant")) monument.Name = "Water Treatment Plant";
else if (m.name.ToLower().Contains("monuments")) Log("WARNING - Invalid monument detected: " + m.name.ToLower());
else Log("WARNING - Monument not detected: " + m.name.ToLower());
if (monument.Name != "Unknown") results.Add(monument);
}
return results;
}
#endregion
#region Image handling
// TODO: Not working due to sandboxing
private void SaveImage(Texture2D texture, string monumentsJson, int seed, int size, string protocol)
{
Log("Saving image..");
// Encode texture into PNG
byte[] bytes = texture.EncodeToPNG();
// TODO: Draw monuments on the map image
// Write the image
File.WriteAllBytes(Rust.Application.dataPath + "/../" + seed + "_" + size + "_" + protocol + ".png", bytes);
Log("Finished saving image");
}
private void UploadImage(Texture2D texture, string monumentsJson, int seed, int size, string protocol)
{
Log("Uploading to: " + UploadUrl);
// Encode texture into a PNG
byte[] bytes = texture.EncodeToPNG();
WWWForm form = new WWWForm();
form.AddField("protocol", protocol);
form.AddField("size", size);
form.AddField("seed", seed);
form.AddField("monuments", monumentsJson);
form.AddField("filename", "map.png");
form.AddField("data", Convert.ToBase64String(bytes));
// Upload
WWW w = new WWW(UploadUrl, form);
}
#endregion
#region Utilities
private static float ConvertWorldToMapCoordinates(float worldPosition, int mapWidth)
{
// NOTE: I'm assuming the worldPosition starts in the middle of the map, meaning 0,
// which is why we're adding half of the map size to it.
float realPosition = (worldPosition + (TerrainMeta.Size.x / 2));
// At this point, we need to figure out how to convert the position from 0-3000 -> 0-2048
float scale = realPosition / TerrainMeta.Size.x;
float mapPosition = scale * mapWidth;
return mapPosition;
}
private static float BlendSubpixel(float top, float bottom, float alphaTop, float alphaBottom)
{
return (top * alphaTop) + ((bottom - 1f) * (alphaBottom - alphaTop));
}
private static Color AlphaBlend(Color top, Color bottom)
{
return new Color(BlendSubpixel(top.r, bottom.r, top.a, bottom.a),
BlendSubpixel(top.g, bottom.g, top.a, bottom.a),
BlendSubpixel(top.b, bottom.b, top.a, bottom.a),
top.a + bottom.a);
}
// TODO: This doesn't seem to work
private static Color InvertColor(Color color)
{
return new Color(1.0f - color.r, 1.0f - color.g, 1.0f - color.b, color.a);
}
public static void Log(string str)
{
Interface.Oxide.LogDebug("SeederyIoMapGen :: " + str);
}
#endregion
}
}