-
Notifications
You must be signed in to change notification settings - Fork 1k
/
BatchCellsParser.cs
148 lines (118 loc) · 5.82 KB
/
BatchCellsParser.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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using NitroxModel.Helper;
using NitroxModel.Logger;
using NitroxServer.GameLogic.Spawning;
using NitroxServer.UnityStubs;
namespace NitroxServer.Serialization
{
/**
* Parses the files in build18 in the format of batch-cells-x-y-z-slot-type.bin
* These files contain serialized GameObjects with EntitySlot components. These
* represent areas that entities (creatures, objects) can spawn within the world.
* This class consolidates the gameObject, entitySlot, and cellHeader data to
* create EntitySpawnPoint objects.
*/
class BatchCellsParser
{
public static readonly Int3 MAP_DIMENSIONS = new Int3(26, 19, 26);
public static readonly Int3 BATCH_DIMENSIONS = new Int3(160, 160, 160);
private readonly ServerProtobufSerializer serializer;
private readonly Dictionary<string, Type> surrogateTypes = new Dictionary<string, Type>();
public BatchCellsParser()
{
serializer = new ServerProtobufSerializer();
surrogateTypes.Add("UnityEngine.Transform", typeof(Transform));
surrogateTypes.Add("UnityEngine.Vector3", typeof(Vector3));
surrogateTypes.Add("UnityEngine.Quaternion", typeof(Quaternion));
}
public List<EntitySpawnPoint> GetEntitySpawnPoints()
{
Log.Info("Loading batch data...");
List<EntitySpawnPoint> entitySpawnPoints = new List<EntitySpawnPoint>();
Parallel.ForEach(Enumerable.Range(1, Map.DIMENSIONS_IN_BATCHES.x), x =>
{
for (int y = 1; y <= Map.DIMENSIONS_IN_BATCHES.y; y++)
{
for (int z = 1; z <= Map.DIMENSIONS_IN_BATCHES.z; z++)
{
Int3 batchId = new Int3(x, y, z);
List<EntitySpawnPoint> batchSpawnPoints = ParseBatchData(batchId);
lock (entitySpawnPoints)
{
entitySpawnPoints.AddRange(batchSpawnPoints);
}
}
}
});
Log.Info("Batch data loaded!");
return entitySpawnPoints;
}
public List<EntitySpawnPoint> ParseBatchData(Int3 batchId)
{
List<EntitySpawnPoint> spawnPoints = new List<EntitySpawnPoint>();
ParseFile(batchId, "", "loot-slots", spawnPoints);
ParseFile(batchId, "", "creature-slots", spawnPoints);
ParseFile(batchId, @"Generated\", "slots", spawnPoints); // Very expensive to load
ParseFile(batchId, "", "loot", spawnPoints);
ParseFile(batchId, "", "creatures", spawnPoints);
ParseFile(batchId, "", "other", spawnPoints);
return spawnPoints;
}
public void ParseFile(Int3 batchId, string pathPrefix, string suffix, List<EntitySpawnPoint> spawnPoints)
{
// This isn't always gonna work.
string path = @"C:\Program Files (x86)\Steam\steamapps\common\Subnautica\SNUnmanagedData\Build18\";
string fileName = path + pathPrefix + "batch-cells-" + batchId.x + "-" + batchId.y + "-" + batchId.z + "-" + suffix + ".bin";
if (!File.Exists(fileName))
{
return;
}
using (Stream stream = File.OpenRead(fileName))
{
CellManager.CellsFileHeader cellsFileHeader = serializer.Deserialize<CellManager.CellsFileHeader>(stream);
for (int cellCounter = 0; cellCounter < cellsFileHeader.numCells; cellCounter++)
{
CellManager.CellHeader cellHeader = serializer.Deserialize<CellManager.CellHeader>(stream);
ProtobufSerializer.LoopHeader gameObjectCount = serializer.Deserialize<ProtobufSerializer.LoopHeader>(stream);
for (int goCounter = 0; goCounter < gameObjectCount.Count; goCounter++)
{
GameObject gameObject = DeserializeGameObject(stream);
EntitySpawnPoint esp = EntitySpawnPoint.From(batchId, gameObject, cellHeader);
spawnPoints.Add(esp);
}
}
}
}
private GameObject DeserializeGameObject(Stream stream)
{
ProtobufSerializer.GameObjectData goData = serializer.Deserialize<ProtobufSerializer.GameObjectData>(stream);
GameObject gameObject = new GameObject(goData);
DeserializeComponents(stream, gameObject);
return gameObject;
}
private void DeserializeComponents(Stream stream, GameObject gameObject)
{
ProtobufSerializer.LoopHeader components = serializer.Deserialize<ProtobufSerializer.LoopHeader>(stream);
for (int componentCounter = 0; componentCounter < components.Count; componentCounter++)
{
ProtobufSerializer.ComponentHeader componentHeader = serializer.Deserialize<ProtobufSerializer.ComponentHeader>(stream);
Type type = null;
if (!surrogateTypes.TryGetValue(componentHeader.TypeName, out type))
{
type = AppDomain.CurrentDomain.GetAssemblies()
.Select(a => a.GetType(componentHeader.TypeName))
.FirstOrDefault(t => t != null);
}
Validate.NotNull(type, $"No type or surrogate found for {componentHeader.TypeName}!");
object component = FormatterServices.GetUninitializedObject(type);
serializer.Deserialize(stream, component, type);
gameObject.AddComponent(component, type);
}
}
}
}