C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Oxide.Core;
using Oxide.Core.Configuration;
using ProtoBuf;
using Apex;
using Rust;
namespace Oxide.Plugins {
[Info("SpawnControl", "Fujikura", "1.3.9", ResourceId = 18)]
class SpawnControl : RustPlugin {
bool Changed = false;
bool _loaded;
bool _startup;
bool _terrainReady;
bool heartbeatOn = false;
double lastMinute;
bool newSave;
bool runningEnforceLimits;
bool runningSpawnKill;
bool runningSpawnLowerDensity;
bool runningSpawnRaiseDensity;
bool runningSpawnFill;
Dictionary <string, object> spawnPopulations = new Dictionary <string, object>();
Dictionary <string, object> spawnGroups = new Dictionary <string, object>();
Dictionary<string, object> spawnDefaults = new Dictionary<string, object>();
Dictionary<string, object> spawnPrefabDefaults = new Dictionary<string, object>();
List<string> PopulationNames = new List<string>();
Dictionary<string, ConvarControlledSpawnPopulation> convarCommands = new Dictionary<string, ConvarControlledSpawnPopulation>();
Dictionary<string, string> populationConvars = new Dictionary<string, string>();
Dictionary <string, int> fillPopulations = new Dictionary <string, int>();
Dictionary <string, int> fillJobs = new Dictionary <string, int>();
DynamicConfigFile defaultSpawnPops;
DynamicConfigFile getFile(string file) => Interface.Oxide.DataFileSystem.GetDatafile($"{file}");
bool chkFile(string file) => Interface.Oxide.DataFileSystem.ExistsDatafile($"{file}");
FieldInfo _numToSpawn = typeof (SpawnPopulation).GetField("numToSpawn", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
FieldInfo _targetDensity = typeof (SpawnPopulation).GetField("_targetDensity", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
FieldInfo _SpawnInstances = typeof (SpawnGroup).GetField("spawnInstances", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
FieldInfo _SpawnDistributions = typeof(SpawnHandler).GetField("SpawnDistributions", (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
FieldInfo _spawnClock = typeof(SpawnGroup).GetField("spawnClock", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo _AllSpawnPopulations = typeof(SpawnHandler).GetField("AllSpawnPopulations", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MethodInfo _SpawnGroupTick = typeof(SpawnHandler).GetMethod("SpawnGroupTick", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MethodInfo _SpawnTick = typeof(SpawnHandler).GetMethod("SpawnTick", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
IEnumerator coSpawnTick;
IEnumerator coSpawnGroupTick;
SpawnHandler _Handle = SpawnHandler?.Instance;
bool enableSpawnGroups;
bool enableGroupTickData;
bool enablePopulationPrefabs;
bool normalizeDefaultVariables;
bool reloadWithIncludedFill;
bool logJobsToConsole;
bool fixBarricadeStacking;
float tickInterval;
int minSpawnsPerTick;
int maxSpawnsPerTick;
object GetConfig(string menu, string datavalue, object defaultValue) {
var data = Config[menu] as Dictionary<string, object>;
if (data == null) {
data = new Dictionary<string, object>();
Config[menu] = data;
Changed = true;
}
object value;
if (!data.TryGetValue(datavalue, out value)) {
value = defaultValue;
data[datavalue] = value;
Changed = true;
}
return value;
}
void LoadVariables() {
spawnPopulations = (Dictionary <string, object>)GetConfig("Spawn", "Population", new Dictionary<string, object>());
spawnGroups = (Dictionary <string, object>)GetConfig("Spawn", "Groups", new Dictionary<string, object>());
enableSpawnGroups = Convert.ToBoolean(GetConfig("Generic", "enableSpawnGroups", false));
enableGroupTickData = Convert.ToBoolean(GetConfig("Generic", "enableGroupTickData", false));
enablePopulationPrefabs = Convert.ToBoolean(GetConfig("Generic", "enablePopulationPrefabs", false));
normalizeDefaultVariables = Convert.ToBoolean(GetConfig("Generic", "normalizeDefaultVariables", true));
reloadWithIncludedFill = Convert.ToBoolean(GetConfig("Generic", "reloadWithIncludedFill", false));
logJobsToConsole = Convert.ToBoolean(GetConfig("Generic", "logJobsToConsole", true));
fixBarricadeStacking = Convert.ToBoolean(GetConfig("Generic", "fixBarricadeStacking", true));
tickInterval = Convert.ToSingle(GetConfig("Handler", "tickInterval", 120.0f));
minSpawnsPerTick = Convert.ToInt32(GetConfig("Handler", "minSpawnsPerTick", 100));
maxSpawnsPerTick = Convert.ToInt32(GetConfig("Handler", "maxSpawnsPerTick", 200));
var configremoval = false;
if ((Config.Get("Animals") as Dictionary<string, object>) != null) {
Config.Remove("Animals");
configremoval = true;
}
if (!Changed && !configremoval) return;
SaveConfig();
Changed = false;
configremoval = false;
}
protected override void LoadDefaultConfig() {
Config.Clear();
LoadVariables();
}
void Init() {
LoadVariables();
_terrainReady = false;
_startup = true;
if (normalizeDefaultVariables) {
ConVar.Spawn.max_density = 1f;
ConVar.Spawn.max_rate = 1f;
ConVar.Spawn.min_density = 0.5f;
ConVar.Spawn.min_rate = 0.5f;
}
newSave = false;
lastMinute = DateTime.UtcNow.Minute;
_Handle = SpawnHandler.Instance;
}
void OnNewSave(string strFilename) {
newSave = true;
}
void Unload() {
ServerMgr.Instance.StopCoroutine("SpawnKill");
ServerMgr.Instance.StopCoroutine("SpawnFill");
ServerMgr.Instance.StopCoroutine("EnforceLimits");
ServerMgr.Instance.StopCoroutine("SpawnLowerDensity");
ServerMgr.Instance.StopCoroutine("SpawnRaiseDensity");
runningEnforceLimits = false;
runningSpawnKill = false;
runningSpawnLowerDensity = false;
runningSpawnRaiseDensity = false;
runningSpawnFill = false;
}
void OnTerrainInitialized() {
if (_startup && !newSave) {
_terrainReady = true;
GetPopulationDefaults();
}
_Handle.MaxSpawnsPerTick = maxSpawnsPerTick;
_Handle.MinSpawnsPerTick = minSpawnsPerTick;
if (spawnPopulations != null && spawnPopulations.Count > 0) {
SpawnPopLoad();
}
else {
SpawnPopDefaults();
}
if (enableSpawnGroups) {
if (spawnGroups != null && spawnGroups.Count > 0) SpawnGroupLoad();
else SpawnGroupDefaults();
}
if (_startup && !newSave) _terrainReady = true;
_loaded = true;
}
void GetPopulationDefaults() {
_Handle = SpawnHandler.Instance;
spawnDefaults = new Dictionary<string, object>();
spawnPrefabDefaults = new Dictionary<string, object>();
foreach (var population in (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null)) {
var prefabdata = new Dictionary<string, object>();
var prefabs = population.Prefabs;
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(population.ResourceFolder)) prefabs = Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", population.ResourceFolder), GameManager.server, PrefabAttribute.server);
if (population.ResourceList != null && population.ResourceList.Length > 0) prefabs = Prefab.Load<Spawnable>(population.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server);
}
var counts = prefabs.GroupBy(x => x.Name).ToDictionary(g => g.Key.ToLower(), g => g.Count());
foreach (var prefab in counts) prefabdata.Add(prefab.Key.ToLower(), prefab.Value );
spawnPrefabDefaults.Add(population.name, prefabdata);
spawnDefaults.Add(population.name, population.TargetDensity);
}
defaultSpawnPops = getFile($"{this.Title}_defaults");
defaultSpawnPops.Clear();
defaultSpawnPops.Set("Backup", spawnDefaults);
defaultSpawnPops.Set("PrefabBackup", spawnPrefabDefaults);
defaultSpawnPops.Save();
}
void LoadPopulationDefaults() {
if (spawnDefaults == null || spawnDefaults.Count == 0) {
spawnDefaults = new Dictionary<string, object>();
defaultSpawnPops = getFile($"{this.Title}_defaults");
spawnDefaults = defaultSpawnPops["Backup"] as Dictionary<string, object>;
spawnPrefabDefaults = defaultSpawnPops["PrefabBackup"] as Dictionary<string, object>;
}
}
void OnServerInitialized() {
_startup = false;
GetPopulationNames();
if (!_loaded) OnTerrainInitialized();
if (fillJobs.Count > 0) Puts($"Planned {fillJobs.Count} job(s) for spawn population");
FixBarricades();
LoadPopulationDefaults();
if (_terrainReady) {
ReloadAnimals();
NextTick(()=> Global.Runner.StartCoroutine(EnforceLimits(true, null)));
}
}
void OnTick() {
if (!_loaded || !heartbeatOn || lastMinute == DateTime.UtcNow.Minute || CheckRoutines() ) return;
lastMinute = DateTime.UtcNow.Minute;
foreach (var job in fillJobs.ToList()) {
if ((int)fillJobs[job.Key] < 1) {
timer.Once(2f, ()=> ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), "sc.fill", new object[] {
job.Key.ToString(), "auto"
}
));
fillJobs[job.Key] = fillPopulations[job.Key];
}
else fillJobs[job.Key] = (int)job.Value -1;
}
}
bool CheckRoutines() {
if (runningEnforceLimits || runningSpawnKill || runningSpawnLowerDensity || runningSpawnRaiseDensity || runningSpawnFill) return true;
return false;
}
void GetPopulationNames() {
_Handle = SpawnHandler.Instance;
PopulationNames = new List<string>();
foreach (var pop in _Handle.AllSpawnPopulations.Where(x => x != null).Distinct().ToList()) PopulationNames.Add(pop.name);
convarCommands = new Dictionary<string, ConvarControlledSpawnPopulation>();
populationConvars = new Dictionary<string, string>();
foreach (var pop in _Handle.ConvarSpawnPopulations.ToList().Where(x => x != null)) {
populationConvars.Add((pop as ConvarControlledSpawnPopulation).PopulationConvar, pop.name);
convarCommands.Add(pop.name, pop as ConvarControlledSpawnPopulation);
}
}
void OnServerCommand(ConsoleSystem.Arg arg) {
if (arg == null || arg.cmd == null || arg.FullString == string.Empty || arg.cmd.FullName == string.Empty) return;
if (populationConvars.ContainsKey(arg.cmd.FullName)) {
float targetDensity = -2;
float.TryParse(arg.Args[0], out targetDensity);
(spawnPopulations[populationConvars[arg.cmd.FullName]] as Dictionary<string, object>)["spawndensity"] = targetDensity;
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
}
}
void SpawnPopDefaults() {
var data = new Dictionary<string, object>();
spawnPopulations.Clear();
foreach (var population in (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null)) {
var populationdata = new Dictionary<string, object>();
populationdata.Add("spawndensity", population.TargetDensity);
populationdata.Add("spawnrate", population.SpawnRate);
populationdata.Add("spawnlimits", population.EnforcePopulationLimits);
populationdata.Add("spawnfillheartbeat", 0);
if (enablePopulationPrefabs) {
var prefabs = population.Prefabs;
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(population.ResourceFolder)) population.Prefabs = Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", population.ResourceFolder), GameManager.server, PrefabAttribute.server);
if (population.ResourceList != null && population.ResourceList.Length > 0) population.Prefabs = Prefab.Load<Spawnable>(population.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server);
prefabs = population.Prefabs;
}
var counts = prefabs.GroupBy(x => x.Name).ToDictionary(g => g.Key.ToLower(), g => g.Count());
var prefabdata = new Dictionary<string, object>();
foreach (var prefab in counts) prefabdata.Add(prefab.Key.ToLower(), prefab.Value );
}
data.Add(population.name, populationdata);
}
spawnPopulations = new Dictionary<string, object>(data);
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
Puts($"Created SpawnPopulation with '{data.Count}' populations.");
}
void SpawnPopLoad() {
Dictionary<string, object> data = new Dictionary<string, object>(spawnPopulations);
if (data == null || data.Count() == 0) {
SpawnPopDefaults();
return;
}
bool changed = false;
if (PopulationNames == null || PopulationNames.Count == 0) foreach (var pop in (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).Where(x => x != null).Distinct().ToList()) PopulationNames.Add(pop.name);
foreach (var population in data.ToList()) {
if (!PopulationNames.Contains(population.Key.ToString())) {
data.Remove(population.Key);
Puts($"Removed old SpawnPopulation: {population.Key}");
changed = true;
continue;
}
}
foreach (var population in (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null)) {
var name = population.name;
if (name == null) continue;
if (!data.ContainsKey(name)) {
var populationdata = new Dictionary<string, object>();
populationdata.Add("spawndensity", population.TargetDensity);
populationdata.Add("spawnrate", population.SpawnRate);
populationdata.Add("spawnlimits", population.EnforcePopulationLimits);
populationdata.Add("spawnfillheartbeat", 0);
if (enablePopulationPrefabs) {
var prefabs = population.Prefabs;
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(population.ResourceFolder)) population.Prefabs = Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", population.ResourceFolder), GameManager.server, PrefabAttribute.server);
if (population.ResourceList != null && population.ResourceList.Length > 0) population.Prefabs = Prefab.Load<Spawnable>(population.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server);
prefabs = population.Prefabs;
}
var counts = prefabs.GroupBy(x => x.Name).ToDictionary(g => g.Key, g => g.Count());
var prefabdata = new Dictionary<string, object>();
foreach (var prefab in counts) prefabdata.Add(prefab.Key, prefab.Value );
populationdata.Add("spawnweights", prefabdata);
}
data.Add(name, populationdata);
Puts($"Added new SpawnPopulation: {name}");
changed = true;
}
}
spawnPopulations = new Dictionary<string, object>(data);
if (changed) {
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
}
bool addedPrefabs = false;
foreach (var spawnPopulation in (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null)) {
object spawndata;
if (!data.TryGetValue(spawnPopulation.name, out spawndata)) continue;
Dictionary<string, object> spawnData = spawndata as Dictionary<string, object>;
if (enablePopulationPrefabs && !spawnData.ContainsKey("spawnweights")) {
var prefabs = spawnPopulation.Prefabs;
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(spawnPopulation.ResourceFolder)) spawnPopulation.Prefabs = Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", spawnPopulation.ResourceFolder), GameManager.server, PrefabAttribute.server);
if (spawnPopulation.ResourceList != null && spawnPopulation.ResourceList.Length > 0) spawnPopulation.Prefabs = Prefab.Load<Spawnable>(spawnPopulation.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server);
prefabs = spawnPopulation.Prefabs;
}
var counts = prefabs.GroupBy(x => x.Name).ToDictionary(g => g.Key.ToLower(), g => g.Count());
var prefabdata = new Dictionary<string, object>();
foreach (var prefab in counts) prefabdata.Add(prefab.Key.ToLower(), prefab.Value );
spawnData.Add("spawnweights", prefabdata);
addedPrefabs = true;
}
if (enablePopulationPrefabs && spawnData.ContainsKey("spawnweights")) {
Dictionary<string, object> prefabData = spawnData["spawnweights"] as Dictionary<string, object>;
var prefabs = new List<Prefab<Spawnable>>();
foreach (var prefab in prefabData.ToList()) {
var gameObject = GameManager.server.FindPrefab(prefab.Key);
if (gameObject == null) {
Puts($"Removed invalid/removed prefab from '{spawnPopulation.name}': {prefab.Key}");
((data[spawnPopulation.name] as Dictionary<string, object>)["spawnweights"] as Dictionary<string, object>).Remove(prefab.Key);
addedPrefabs = true;
}
var component = gameObject?.GetComponent<Spawnable>();
if (component == null) continue;
for (var i = 0; i < Convert.ToInt32(prefab.Value);
i++) prefabs.Add(new Prefab<Spawnable>(prefab.Key.ToLower(), gameObject, component, GameManager.server, PrefabAttribute.server));
}
spawnPopulation.Prefabs= prefabs.ToArray();
_numToSpawn.SetValue(spawnPopulation, new int[prefabs.ToArray().Length]);
}
spawnPopulation.EnforcePopulationLimits = Convert.ToBoolean(spawnData["spawnlimits"]);
spawnPopulation.SpawnRate = Convert.ToSingle(spawnData["spawnrate"]);
_targetDensity.SetValue(spawnPopulation, Convert.ToSingle(spawnData["spawndensity"]));
SetAnimal(spawnPopulation.name, Convert.ToSingle(spawnData["spawndensity"]));
if ((int)spawnData["spawnfillheartbeat"] > 0 ) {
fillPopulations.Add(spawnPopulation.name, (int)spawnData["spawnfillheartbeat"]);
heartbeatOn = true;
}
}
if (addedPrefabs) {
spawnPopulations = new Dictionary<string, object>(data);
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
}
if (heartbeatOn) fillJobs = new Dictionary<string, int>(fillPopulations);
Puts($"Loaded '{data.Count}' SpawnPopulations");
}
void ReloadAnimals() {
Dictionary<string, object> data = new Dictionary<string, object>(spawnPopulations);
if (data == null || data.Count() == 0) {
SpawnPopDefaults();
return;
}
foreach (var spawnPopulation in (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null)) {
object spawndata;
if (!data.TryGetValue(spawnPopulation.name, out spawndata)) continue;
if (!convarCommands.ContainsKey(spawnPopulation.name)) continue;
Dictionary<string, object> spawnData = spawndata as Dictionary<string, object>;
spawnPopulation.EnforcePopulationLimits = Convert.ToBoolean(spawnData["spawnlimits"]);
spawnPopulation.SpawnRate = Convert.ToSingle(spawnData["spawnrate"]);
_targetDensity.SetValue(spawnPopulation, Convert.ToSingle(spawnData["spawndensity"]));
SetAnimal(spawnPopulation.name, Convert.ToSingle(spawnData["spawndensity"]));
}
}
void SetAnimal(string name, float value) {
if (convarCommands.ContainsKey(name)) ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"{convarCommands[name].PopulationConvar} {value.ToString()}");
}
void SpawnGroupDefaults() {
if (!enableSpawnGroups) return;
spawnGroups.Clear();
foreach (var monument in UnityEngine.Object.FindObjectsOfType<MonumentInfo>()) {
if (spawnGroups.ContainsKey(PrefabCut(monument.name))) continue;
foreach(var group in monument.gameObject.GetComponentsInChildren<SpawnGroup>()) {
if (group.name.StartsWith("door_barricade") || group.name.StartsWith("Barricades") || group.name.StartsWith("Ore")) continue;
Dictionary <string, object> spawner = new Dictionary <string, object>();
spawner.Add("_refName", group.name);
spawner.Add("_refNumber", group.maxPopulation);
spawner.Add("delayMin", group.respawnDelayMin);
spawner.Add("delayMax", group.respawnDelayMax);
if (enableGroupTickData) {
spawner.Add("perTickMin", group.numToSpawnPerTickMin);
spawner.Add("perTickMax", group.numToSpawnPerTickMax);
}
if (!spawnGroups.ContainsKey(PrefabCut(monument.name))) spawnGroups.Add(PrefabCut(monument.name), new List<object>());
(spawnGroups[PrefabCut(monument.name)] as List<object>).Add(spawner);
}
}
Config["Spawn", "Groups"] = spawnGroups;
Config.Save();
Puts($"Created '{spawnGroups.Count()}' spawnGroups");
}
void SpawnGroupLoad() {
if (!enableSpawnGroups) return;
if (spawnGroups == null || spawnGroups.Count() == 0) {
SpawnGroupDefaults();
return;
}
bool addedMonument = false;
bool addedGroup = false;
bool addedTickData = false;
List<string> alreadySeenMonuments = new List<string>();
foreach (var monument in UnityEngine.Object.FindObjectsOfType<MonumentInfo>()) {
if (alreadySeenMonuments.Contains(monument.name)) continue;
alreadySeenMonuments.Add(monument.name);
MethodInfo spawnMethod = null;
if (!spawnGroups.ContainsKey(PrefabCut(monument.name))) {
foreach(var group in monument.gameObject.GetComponentsInChildren<SpawnGroup>()) {
if (group.name.StartsWith("door_barricade") || group.name.StartsWith("Barricades") || group.name.StartsWith("Ore")) continue;
Dictionary <string, object> spawner = new Dictionary <string, object>();
spawner.Add("_refName", group.name);
spawner.Add("_refNumber", group.maxPopulation);
spawner.Add("delayMin", group.respawnDelayMin);
spawner.Add("delayMax", group.respawnDelayMax);
if (enableGroupTickData) {
spawner.Add("perTickMin", group.numToSpawnPerTickMin);
spawner.Add("perTickMax", group.numToSpawnPerTickMax);
}
if (!spawnGroups.ContainsKey(PrefabCut(monument.name))) {
spawnGroups.Add(PrefabCut(monument.name), new List<object>());
addedMonument = true;
}
(spawnGroups[PrefabCut(monument.name)] as List<object>).Add(spawner);
}
continue;
}
List<object> filegroups = (spawnGroups[PrefabCut(monument.name)] as List<object>);
foreach(var group in monument.gameObject.GetComponentsInChildren<SpawnGroup>()) {
if (group.name.StartsWith("door_barricade") || group.name.StartsWith("Barricades") || group.name.StartsWith("Ore")) continue;
bool groupMatch = false;
foreach (var filegroup in filegroups) {
Dictionary<string, object> groupdata = filegroup as Dictionary<string, object>;
if ((string)groupdata["_refName"] == group.name && (int)groupdata["_refNumber"] == group.maxPopulation) {
group.respawnDelayMin = Convert.ToSingle(groupdata["delayMin"]);
group.respawnDelayMax = Convert.ToSingle(groupdata["delayMax"]);
if (enableGroupTickData) {
if (!groupdata.ContainsKey("perTickMin")) {
groupdata.Add("perTickMin", group.numToSpawnPerTickMin);
addedTickData = true;
}
if (!groupdata.ContainsKey("perTickMax")) {
groupdata.Add("perTickMax", group.numToSpawnPerTickMax);
addedTickData = true;
}
group.numToSpawnPerTickMin = (int)groupdata["perTickMin"];
group.numToSpawnPerTickMax = (int)groupdata["perTickMax"];
}
LocalClock spawnClock = (LocalClock)_spawnClock.GetValue(group);
if (spawnClock.events != null && spawnClock.events.Count > 0) {
Action action = new Action(spawnClock.events.First().action);
spawnClock = new LocalClock();
spawnClock.Add(group.GetSpawnDelta(), group.GetSpawnVariance(), action);
spawnClock.Tick();
_spawnClock.SetValue(group, spawnClock);
}
groupMatch = true;
}
}
if (!groupMatch) {
Dictionary <string, object> spawner = new Dictionary <string, object>();
spawner.Add("_refName", group.name);
spawner.Add("_refNumber", group.maxPopulation);
spawner.Add("delayMin", group.respawnDelayMin);
spawner.Add("delayMax", group.respawnDelayMax);
if (enableGroupTickData) {
spawner.Add("perTickMin", group.numToSpawnPerTickMin);
spawner.Add("perTickMax", group.numToSpawnPerTickMax);
}
(spawnGroups[PrefabCut(monument.name)] as List<object>).Add(spawner);
addedGroup = true;
}
}
}
if (addedMonument || addedGroup || addedTickData) {
Config["Spawn", "Groups"] = spawnGroups;
Config.Save();
}
Puts($"Loaded '{spawnGroups.Count()}' spawnGroups");
_Handle.StartSpawnTick();
}
void FixBarricades() {
if (fixBarricadeStacking) return;
var spawns = Resources.FindObjectsOfTypeAll<BaseEntity>().Where(c => c.isActiveAndEnabled && c.ShortPrefabName.StartsWith("door_barricade")).OrderBy(c => c.transform.position.x).ThenBy(c => c.transform.position.z).ThenBy(c => c.transform.position.z).ToList();
var count = spawns.Count();
var racelimit = count * count;
var antirace = 0;
var deleted = 0;
for (var i = 0; i < count; i++) {
var box = spawns[i];
var next = i + 1;
var pos = new Vector2(box.transform.position.x, box.transform.position.z);
if (++antirace > racelimit) return;
while (next < count) {
var box2 = spawns[next];
var pos2 = new Vector2(box2.transform.position.x, box2.transform.position.z);
var distance = Vector2.Distance(pos, pos2);
if (++antirace > racelimit) return;
if (distance < 0.5f) {
spawns.RemoveAt(next);
count--;
(box2 as BaseEntity).Kill();
deleted++;
}
else break;
}
}
if (deleted > 0) Puts($"Removed {deleted} stacked DoorBarricades.");
}
void OnEntitySpawned(BaseNetworkable entity) {
if (!_loaded || !fixBarricadeStacking || entity == null || !(entity as BaseEntity).ShortPrefabName.StartsWith("door_barricade")) return;
NextTick(()=> {
if (entity == null) return;
BaseEntity[] stacked = null;
try {
stacked = Physics.OverlapSphere(entity.transform.position, 0.5f).Where(entry => entry != null && entry.GetComponentInParent<BaseEntity>() != null && entry.GetComponentInParent<BaseEntity>().ShortPrefabName.StartsWith("door_barricade")).Select(entry => entry.GetComponentInParent<BaseEntity>()).Distinct().OrderByDescending(c => c.net.ID).ToArray();
}
catch {
}
if (stacked != null && stacked.Length > 1) for (int i = 0; i < stacked.Length - 1; i++) stacked[i].Kill();
}
);
}
[ConsoleCommand("sc.cleardata")]
void ccmdClearData(ConsoleSystem.Arg arg) {
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
string data = string.Empty;
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to provide 'weights', 'population', 'groups' or 'all'");
return;
}
data = arg.Args[0];
if (data == "weights") {
foreach (var pop in spawnPopulations) if ((spawnPopulations[pop.Key] as Dictionary <string, object>).ContainsKey("spawnweights")) (spawnPopulations[pop.Key] as Dictionary <string, object>).Remove("spawnweights");
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
SendReply(arg, "Did reset weights data");
}
else if (data == "population") {
spawnPopulations.Clear();
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
SendReply(arg, "Did reset population data.");
}
else if (data == "groups") {
spawnGroups.Clear();
Config["Spawn", "Groups"] = spawnGroups;
Config.Save();
SendReply(arg, "Did reset groups data");
}
else if (data == "all") {
spawnGroups.Clear();
spawnPopulations.Clear();
Config["Spawn", "Groups"] = spawnGroups;
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
SendReply(arg, "Did reset all data.Next server boot loads the game defaults");
}
else {
SendReply(arg, "You need to provide 'weights', 'population', 'groups' or 'all'");
return;
}
SendReply(arg, "Next server boot writes the game defaults back into the config.");
}
[ConsoleCommand("sc.tickinterval")]
void ccmdTickInterval(ConsoleSystem.Arg arg) {
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
var _handler = _Handle;
float currTickInterval = _handler.TickInterval;
if (arg.Args == null) {
SendReply(arg, $"Current TickInterval is '{currTickInterval}' seconds");
return;
}
if (arg.Args != null && arg.Args.Length >0) {
float input = 0f;
if (float.TryParse(arg.Args[0], out input)) {
currTickInterval = input;
SendReply(arg, $"TickInterval set to '{currTickInterval}' seconds");
_handler.TickInterval = currTickInterval;
if (currTickInterval != tickInterval) {
tickInterval = currTickInterval;
Config["Handler", "tickInterval"] = tickInterval;
Config.Save();
}
return;
}
else {
SendReply(arg, "You need to provide a full number in seconds");
return;
}
}
}
[ConsoleCommand("sc.fill")]
void ccmdSpawnFill(ConsoleSystem.Arg arg) {
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
string populationname = string.Empty;
bool single = false;
if (arg.Args != null && arg.Args.Length >0) {
populationname = arg.Args[0];
single = true;
}
Global.Runner.StartCoroutine(SpawnFill(populationname, single, arg));
}
IEnumerator SpawnFill(string populationname, bool single, ConsoleSystem.Arg arg) {
runningSpawnFill = true;
yield return new WaitWhile(new System.Func<bool>(() => runningEnforceLimits));
var allSpawnPopulations = (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null).ToArray();
bool findany = false;
if (single) {
bool fullMatch = false;
SpawnDistribution[] spawndists = (SpawnDistribution[])_SpawnDistributions.GetValue(_Handle);
for (int i = 0; i < allSpawnPopulations.Length; i++) {
if (!(allSpawnPopulations[i] == null)) {
if ((allSpawnPopulations[i].name.StartsWith(populationname) || allSpawnPopulations[i].name.EndsWith(populationname)) && !fullMatch) {
if (allSpawnPopulations[i].name == populationname) fullMatch = true;
int oldCount = _Handle.GetCurrentCount(allSpawnPopulations[i], spawndists[i]);
_Handle.SpawnInitial(allSpawnPopulations[i], spawndists[i]);
if (arg.Args.Length > 1 && arg.Args[1] == "auto") if (logJobsToConsole) SendReply(arg, $"Timed population of '{allSpawnPopulations[i].name}' | Old: {oldCount} | New: {_Handle.GetCurrentCount(allSpawnPopulations[i], spawndists[i])}");
if (arg.Args.Length == 1) SendReply(arg, $"Forced population of '{allSpawnPopulations[i].name}' | Old: {oldCount} | New: {_Handle.GetCurrentCount(allSpawnPopulations[i], spawndists[i])}");
findany = true;
yield return new WaitForEndOfFrame();
}
}
}
if (!findany) SendReply(arg, $"SpawnPopulation '{populationname}' not found");
}
else {
int oldCount = 0;
int newCount = 0;
SendReply(arg, "Filling All SpawnPopulations");
SpawnDistribution[] spawndists = (SpawnDistribution[])_SpawnDistributions.GetValue(_Handle);
for (int i = 0; i < allSpawnPopulations.Length; i++) {
if (!(allSpawnPopulations[i] == null)) {
oldCount += _Handle.GetCurrentCount(allSpawnPopulations[i], spawndists[i]);
_Handle.SpawnInitial(allSpawnPopulations[i], spawndists[i]);
newCount += _Handle.GetCurrentCount(allSpawnPopulations[i], spawndists[i]);
yield return new WaitForEndOfFrame();
}
}
SendReply(arg, $"Filled SpawnPopulations | Old: {oldCount} | New: {newCount}");
}
_Handle.FillGroups();
runningSpawnFill = false;
yield return null;
}
[ConsoleCommand("sc.raisedensity")]
void ccmdSpawnRaiseDensity(ConsoleSystem.Arg arg) {
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
float percentage = 0f;
string populationname = string.Empty;
if (arg.Args == null || arg.Args.Length < 2) {
SendReply(arg, "You need to provide a number as percantage and a valid SpawnPopulation name");
return;
}
float percent = -1;
if (float.TryParse(arg.Args[0], out percent)) percentage = percent;
else {
SendReply(arg, "You need to provide a number as percentage");
return;
}
if (percentage > 100f) {
percentage = 100f;
SendReply(arg, "Percentage was limited to 100% to avoid server freezes");
}
populationname = arg.Args[1];
bool raiseAll = false;
if (populationname == "all") raiseAll = true;
SendReply(arg, $"Calling raise of density with '{percentage}' percent for '{populationname}'");
Global.Runner.StartCoroutine(SpawnRaiseDensity(percentage, populationname, raiseAll, arg));
}
IEnumerator SpawnRaiseDensity(float percentage, string populationname, bool raiseAll, ConsoleSystem.Arg arg) {
runningSpawnRaiseDensity = true;
var allSpawnPopulations = (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null).ToArray();
var filter = Oxide.Game.Rust.RustExtension.Filter.ToList();
filter.Add("Failed to create agent because there is no valid NavMesh");
Oxide.Game.Rust.RustExtension.Filter = filter.ToArray();
bool findany = false;
if (!raiseAll) {
bool fullMatch = false;
for (int i = 0; i < allSpawnPopulations.Length; i++) {
if (!(allSpawnPopulations[i] == null)) {
if ((allSpawnPopulations[i].name.StartsWith(populationname) || allSpawnPopulations[i].name.EndsWith(populationname)) && !fullMatch) {
if (allSpawnPopulations[i].name == populationname) fullMatch = true;
float oldDensity = allSpawnPopulations[i].TargetDensity;
float increaseDensity = 0f;
if (oldDensity > 0f) increaseDensity = allSpawnPopulations[i].TargetDensity / 100 * percentage;
else {
object backupDensity = null;
if (spawnDefaults.TryGetValue(allSpawnPopulations[i].name, out backupDensity)) increaseDensity = Convert.ToSingle(backupDensity) / 100 * percentage;
else increaseDensity = allSpawnPopulations[i].TargetDensity / 100 * percentage;
}
float newDensity = (float)Math.Round((double)(oldDensity+increaseDensity));
_targetDensity.SetValue(allSpawnPopulations[i], newDensity);
SetAnimal(allSpawnPopulations[i].name, newDensity);
(spawnPopulations[allSpawnPopulations[i].name] as Dictionary<string, object>)["spawndensity"] = allSpawnPopulations[i].TargetDensity;
SendReply(arg, $"Raised density of '{allSpawnPopulations[i].name}' | Old: {oldDensity} | New: {newDensity}");
findany = true;
}
}
}
if (!findany) SendReply(arg, $"SpawnPopulation '{populationname}' not found");
else {
SendReply(arg, "New raised density's set and saved.");
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
string type = arg.Args[1];
arg.Args = new string[] {
type
}
;
NextTick(() => ccmdSpawnFill(arg));
}
}
else {
SendReply(arg, $"Raising all SpawnPopulation density's by '{percentage}' percent");
for (int i = 0; i < allSpawnPopulations.Length; i++) {
if (!(allSpawnPopulations[i] == null)) {
float oldDensity = allSpawnPopulations[i].TargetDensity;
float increaseDensity = allSpawnPopulations[i].TargetDensity / 100 * percentage;
float newDensity = (float)Math.Round((double)(oldDensity+increaseDensity));
_targetDensity.SetValue(allSpawnPopulations[i], newDensity);
SetAnimal(allSpawnPopulations[i].name, newDensity);
(spawnPopulations[allSpawnPopulations[i].name] as Dictionary<string, object>)["spawndensity"] = allSpawnPopulations[i].TargetDensity;
}
}
SendReply(arg, "New raised density's set and saved.");
Config["Spawn", "Population"] = spawnPopulations;
Config.Save();
arg.Args = null;
NextTick(() => ccmdSpawnFill(arg));
}
runningSpawnRaiseDensity = false;
filter = Oxide.Game.Rust.RustExtension.Filter.ToList();
filter.Remove("Failed to create agent because there is no valid NavMesh");
Oxide.Game.Rust.RustExtension.Filter = filter.ToArray();
yield return null;
}
[ConsoleCommand("sc.lowerdensity")]
void ccmdSpawnLowerDensity(ConsoleSystem.Arg arg) {
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
float percentage = 0f;
string populationname = string.Empty;
if (arg.Args == null || arg.Args.Length < 2) {
SendReply(arg, "You need to provide a percentage number and a valid SpawnPopulation name");
return;
}
float percent = -1;
if (float.TryParse(arg.Args[0], out percent)) percentage = percent;
else {
SendReply(arg, "You need to provide a number as percentage");
return;
}
populationname = arg.Args[1];
bool lowerAll = false;
if (populationname == "all") lowerAll = true;
if (percentage > 100f) {
percentage = 100f;
SendReply(arg, "Percentage was limited to 100%");
}
bool force = false;
if (arg.Args.Length >= 3 && arg.Args[2] == "force") force = true;
if (lowerAll && percentage == 100f && !force) {
SendReply(arg, "Canceled: Lowering all by 100% would set all to zero pop");
SendReply(arg, "You need to add 'force' as additional argument!");
return;
}
SendReply(arg, $"Calling lower with '{percentage}' percent for '{populationname}'");
Global.Runner.StartCoroutine(SpawnLowerDensity(percentage, populationname, lowerAll, arg));
}
IEnumerator SpawnLowerDensity(float percentage, string populationname, bool lowerAll, ConsoleSystem.Arg arg) {
runningSpawnLowerDensity = true;
var allSpawnPopulations = (_AllSpawnPopulations.GetValue(_Handle) as SpawnPopulation[]).ToList().Where(x => x != null).ToArray();
var filter = Oxide.Game.Rust.RustExtension.Filter.ToList();
filter.Add("Failed to create agent because there is no valid NavMesh");
Oxide.Game.Rust.RustExtension.Filter = filter.ToArray();
bool findany = false;
if (!lowerAll) {
bool fullMatch = false;
for (int i = 0; i < allSpawnPopulations.Length; i++) {
if (!(allSpawnPopulations[i] == null)) {
if ((allSpawnPopulations[i
// ... [Code preview truncated due to length. Full source in attached file] ...