356 lines
8.8 KiB
C#
356 lines
8.8 KiB
C#
using System.Collections.Generic;
|
|
using ConVar;
|
|
using Facepunch;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
|
|
public class ServerBuildingManager : BuildingManager
|
|
{
|
|
private int decayTickBuildingIndex;
|
|
|
|
private int decayTickEntityIndex;
|
|
|
|
private int decayTickWorldIndex;
|
|
|
|
private int navmeshCarveTickBuildingIndex;
|
|
|
|
private uint maxBuildingID;
|
|
|
|
public void CheckSplit(DecayEntity ent)
|
|
{
|
|
if (ent.buildingID != 0)
|
|
{
|
|
Building building = ent.GetBuilding();
|
|
if (building != null && ShouldSplit(building))
|
|
{
|
|
Split(building);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool ShouldSplit(Building building)
|
|
{
|
|
if (building.HasBuildingBlocks())
|
|
{
|
|
building.buildingBlocks[0].EntityLinkBroadcast();
|
|
foreach (BuildingBlock buildingBlock in building.buildingBlocks)
|
|
{
|
|
if (!buildingBlock.ReceivedEntityLinkBroadcast())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void Split(Building oldBuilding)
|
|
{
|
|
List<Building> obj = Facepunch.Pool.Get<List<Building>>();
|
|
Building largestSplit = null;
|
|
while (oldBuilding.HasBuildingBlocks())
|
|
{
|
|
BuildingBlock buildingBlock = oldBuilding.buildingBlocks[0];
|
|
uint newID = BuildingManager.server.NewBuildingID();
|
|
buildingBlock.EntityLinkBroadcast(delegate(BuildingBlock b)
|
|
{
|
|
b.AttachToBuilding(newID);
|
|
});
|
|
Building building = BuildingManager.server.GetBuilding(newID);
|
|
if (building != null)
|
|
{
|
|
obj.Add(building);
|
|
}
|
|
}
|
|
int num = 0;
|
|
foreach (Building item in obj)
|
|
{
|
|
if (item.buildingBlocks.Count > num)
|
|
{
|
|
num = item.buildingBlocks.Count;
|
|
largestSplit = item;
|
|
}
|
|
}
|
|
SplitEntities(oldBuilding.buildingPrivileges, largestSplit);
|
|
SplitEntities(oldBuilding.decayEntities, largestSplit);
|
|
if (AI.nav_carve_use_building_optimization)
|
|
{
|
|
oldBuilding.isNavMeshCarvingDirty = true;
|
|
int ticks = 2;
|
|
UpdateNavMeshCarver(oldBuilding, ref ticks, 0);
|
|
}
|
|
Facepunch.Pool.FreeUnmanaged(ref obj);
|
|
}
|
|
|
|
private static void SplitEntities<T>(ListHashSet<T> input, Building largestSplit) where T : DecayEntity
|
|
{
|
|
List<T> obj = Facepunch.Pool.Get<List<T>>();
|
|
obj.AddRange(input);
|
|
foreach (T item in obj)
|
|
{
|
|
BuildingBlock nearbyBuildingBlock = item.GetNearbyBuildingBlock();
|
|
uint num = ((item is ContainerCorpse || item.GetParentEntity() is ContainerCorpse) ? largestSplit.ID : 0u);
|
|
item.AttachToBuilding(nearbyBuildingBlock ? nearbyBuildingBlock.buildingID : num);
|
|
}
|
|
Facepunch.Pool.FreeUnmanaged(ref obj);
|
|
}
|
|
|
|
public void CheckMerge(DecayEntity ent)
|
|
{
|
|
if (ent.buildingID == 0)
|
|
{
|
|
return;
|
|
}
|
|
Building building = ent.GetBuilding();
|
|
if (building == null)
|
|
{
|
|
return;
|
|
}
|
|
ent.EntityLinkMessage(delegate(BuildingBlock b)
|
|
{
|
|
if (b.buildingID != building.ID)
|
|
{
|
|
Building building2 = b.GetBuilding();
|
|
if (building2 != null)
|
|
{
|
|
Merge(building, building2);
|
|
}
|
|
}
|
|
});
|
|
if (AI.nav_carve_use_building_optimization)
|
|
{
|
|
building.isNavMeshCarvingDirty = true;
|
|
int ticks = 2;
|
|
UpdateNavMeshCarver(building, ref ticks, 0);
|
|
}
|
|
}
|
|
|
|
private void Merge(Building building1, Building building2)
|
|
{
|
|
while (building2.HasDecayEntities())
|
|
{
|
|
building2.decayEntities[0].AttachToBuilding(building1.ID);
|
|
}
|
|
if (AI.nav_carve_use_building_optimization)
|
|
{
|
|
building1.isNavMeshCarvingDirty = true;
|
|
building2.isNavMeshCarvingDirty = true;
|
|
int ticks = 3;
|
|
UpdateNavMeshCarver(building1, ref ticks, 0);
|
|
UpdateNavMeshCarver(building1, ref ticks, 0);
|
|
}
|
|
}
|
|
|
|
public void Cycle()
|
|
{
|
|
using (TimeWarning.New("StabilityCheckQueue"))
|
|
{
|
|
StabilityEntity.stabilityCheckQueue.RunQueue(Stability.stabilityqueue);
|
|
}
|
|
using (TimeWarning.New("UpdateSurroundingsQueue"))
|
|
{
|
|
StabilityEntity.updateSurroundingsQueue.RunQueue(Stability.surroundingsqueue);
|
|
}
|
|
using (TimeWarning.New("UpdateSkinQueue"))
|
|
{
|
|
BuildingBlock.updateSkinQueueServer.RunQueue(1.0);
|
|
}
|
|
using (TimeWarning.New("BuildingDecayTick"))
|
|
{
|
|
int num = 5;
|
|
BufferList<Building> values = buildingDictionary.Values;
|
|
for (int i = decayTickBuildingIndex; i < values.Count; i++)
|
|
{
|
|
if (num <= 0)
|
|
{
|
|
break;
|
|
}
|
|
BufferList<DecayEntity> values2 = values[i].decayEntities.Values;
|
|
for (int j = decayTickEntityIndex; j < values2.Count; j++)
|
|
{
|
|
if (num <= 0)
|
|
{
|
|
break;
|
|
}
|
|
values2[j].DecayTick();
|
|
num--;
|
|
if (num <= 0)
|
|
{
|
|
decayTickBuildingIndex = i;
|
|
decayTickEntityIndex = j;
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
decayTickEntityIndex = 0;
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
decayTickBuildingIndex = 0;
|
|
}
|
|
}
|
|
using (TimeWarning.New("WorldDecayTick"))
|
|
{
|
|
int num2 = 5;
|
|
BufferList<DecayEntity> values3 = decayEntities.Values;
|
|
for (int k = decayTickWorldIndex; k < values3.Count; k++)
|
|
{
|
|
if (num2 <= 0)
|
|
{
|
|
break;
|
|
}
|
|
values3[k].DecayTick();
|
|
num2--;
|
|
if (num2 <= 0)
|
|
{
|
|
decayTickWorldIndex = k;
|
|
}
|
|
}
|
|
if (num2 > 0)
|
|
{
|
|
decayTickWorldIndex = 0;
|
|
}
|
|
}
|
|
if (!AI.nav_carve_use_building_optimization)
|
|
{
|
|
return;
|
|
}
|
|
using (TimeWarning.New("NavMeshCarving"))
|
|
{
|
|
int ticks = 5;
|
|
BufferList<Building> values4 = buildingDictionary.Values;
|
|
for (int l = navmeshCarveTickBuildingIndex; l < values4.Count; l++)
|
|
{
|
|
if (ticks <= 0)
|
|
{
|
|
break;
|
|
}
|
|
Building building = values4[l];
|
|
UpdateNavMeshCarver(building, ref ticks, l);
|
|
}
|
|
if (ticks > 0)
|
|
{
|
|
navmeshCarveTickBuildingIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UpdateNavMeshCarver(Building building, ref int ticks, int i)
|
|
{
|
|
if (!AI.nav_carve_use_building_optimization || (!building.isNavMeshCarveOptimized && building.navmeshCarvers.Count < AI.nav_carve_min_building_blocks_to_apply_optimization) || !building.isNavMeshCarvingDirty)
|
|
{
|
|
return;
|
|
}
|
|
building.isNavMeshCarvingDirty = false;
|
|
if (building.navmeshCarvers == null)
|
|
{
|
|
if (building.buildingNavMeshObstacle != null)
|
|
{
|
|
Object.Destroy(building.buildingNavMeshObstacle.gameObject);
|
|
building.buildingNavMeshObstacle = null;
|
|
building.isNavMeshCarveOptimized = false;
|
|
}
|
|
return;
|
|
}
|
|
Vector3 vector = new Vector3(World.Size, World.Size, World.Size);
|
|
Vector3 vector2 = new Vector3(0L - (long)World.Size, 0L - (long)World.Size, 0L - (long)World.Size);
|
|
int count = building.navmeshCarvers.Count;
|
|
if (count > 0)
|
|
{
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
NavMeshObstacle navMeshObstacle = building.navmeshCarvers[j];
|
|
if (navMeshObstacle.enabled)
|
|
{
|
|
navMeshObstacle.enabled = false;
|
|
}
|
|
for (int k = 0; k < 3; k++)
|
|
{
|
|
if (navMeshObstacle.transform.position[k] < vector[k])
|
|
{
|
|
vector[k] = navMeshObstacle.transform.position[k];
|
|
}
|
|
if (navMeshObstacle.transform.position[k] > vector2[k])
|
|
{
|
|
vector2[k] = navMeshObstacle.transform.position[k];
|
|
}
|
|
}
|
|
}
|
|
Vector3 position = (vector2 + vector) * 0.5f;
|
|
Vector3 zero = Vector3.zero;
|
|
float num = Mathf.Abs(position.x - vector.x);
|
|
float num2 = Mathf.Abs(position.y - vector.y);
|
|
float num3 = Mathf.Abs(position.z - vector.z);
|
|
float num4 = Mathf.Abs(vector2.x - position.x);
|
|
float num5 = Mathf.Abs(vector2.y - position.y);
|
|
float num6 = Mathf.Abs(vector2.z - position.z);
|
|
zero.x = Mathf.Max((num > num4) ? num : num4, AI.nav_carve_min_base_size);
|
|
zero.y = Mathf.Max((num2 > num5) ? num2 : num5, AI.nav_carve_min_base_size);
|
|
zero.z = Mathf.Max((num3 > num6) ? num3 : num6, AI.nav_carve_min_base_size);
|
|
if (count < 10)
|
|
{
|
|
zero *= AI.nav_carve_size_multiplier;
|
|
}
|
|
else
|
|
{
|
|
zero *= AI.nav_carve_size_multiplier - 1f;
|
|
}
|
|
if (building.navmeshCarvers.Count > 0)
|
|
{
|
|
if (building.buildingNavMeshObstacle == null)
|
|
{
|
|
building.buildingNavMeshObstacle = new GameObject($"Building ({building.ID}) NavMesh Carver").AddComponent<NavMeshObstacle>();
|
|
building.buildingNavMeshObstacle.enabled = false;
|
|
building.buildingNavMeshObstacle.carving = true;
|
|
building.buildingNavMeshObstacle.shape = NavMeshObstacleShape.Box;
|
|
building.buildingNavMeshObstacle.height = AI.nav_carve_height;
|
|
building.isNavMeshCarveOptimized = true;
|
|
}
|
|
if (building.buildingNavMeshObstacle != null)
|
|
{
|
|
building.buildingNavMeshObstacle.transform.position = position;
|
|
building.buildingNavMeshObstacle.size = zero;
|
|
if (!building.buildingNavMeshObstacle.enabled)
|
|
{
|
|
building.buildingNavMeshObstacle.enabled = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (building.buildingNavMeshObstacle != null)
|
|
{
|
|
Object.Destroy(building.buildingNavMeshObstacle.gameObject);
|
|
building.buildingNavMeshObstacle = null;
|
|
building.isNavMeshCarveOptimized = false;
|
|
}
|
|
ticks--;
|
|
if (ticks <= 0)
|
|
{
|
|
navmeshCarveTickBuildingIndex = i;
|
|
}
|
|
}
|
|
|
|
public uint NewBuildingID()
|
|
{
|
|
return ++maxBuildingID;
|
|
}
|
|
|
|
public void LoadBuildingID(uint id)
|
|
{
|
|
maxBuildingID = Mathx.Max(maxBuildingID, id);
|
|
}
|
|
|
|
protected override Building CreateBuilding(uint id)
|
|
{
|
|
return new Building
|
|
{
|
|
ID = id
|
|
};
|
|
}
|
|
|
|
protected override void DisposeBuilding(ref Building building)
|
|
{
|
|
building = null;
|
|
}
|
|
}
|