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;
namespace Oxide.Plugins {
[Info("SpawnControl", "Fujikura", "1.0.6", 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> SpawnPopulation = new Dictionary <string, object>();
Dictionary <string, object> SpawnGroups = new Dictionary <string, object>();
Dictionary<string, object> spawnDefaults = new Dictionary<string, object>();
List<string> PopulationNames = new List<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 _PrefabsField = typeof (SpawnPopulation).GetField("Prefabs", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo _targetDensity = typeof (SpawnPopulation).GetField("_targetDensity", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo _SpawnInstances = typeof (SpawnGroup).GetField("spawnInstances", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo _SpawnDistributions = typeof(SpawnHandler).GetField("SpawnDistributions", (BindingFlags.Instance | BindingFlags.NonPublic));
FieldInfo _spawnClock = typeof(SpawnGroup).GetField("spawnClock", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo _OnDestroy = typeof(SpawnPointInstance).GetMethod("OnDestroy", BindingFlags.NonPublic | BindingFlags.Instance);
bool enableSpawnGroups;
bool enableGroupTickData;
bool enablePopulationPrefabs;
bool normalizeDefaultVariables;
bool initialFillPopulations;
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() {
SpawnPopulation = (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));
initialFillPopulations = Convert.ToBoolean(GetConfig("Generic", "initialFillPopulations", 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));
if (!Changed) return;
SaveConfig();
Changed = 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;
}
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();
}
SpawnHandler.Instance.MaxSpawnsPerTick = maxSpawnsPerTick;
SpawnHandler.Instance.MinSpawnsPerTick = minSpawnsPerTick;
if (SpawnHandler.Instance.TickInterval != tickInterval) {
SpawnHandler.Instance.StopCoroutine("SpawnTick");
SpawnHandler.Instance.TickInterval = tickInterval;
SpawnHandler.Instance.StartCoroutine("SpawnTick");
}
if (SpawnPopulation != null && SpawnPopulation.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() {
List<SpawnPopulation> spawnPopulations = FileSystem.LoadAll<SpawnPopulation>("Assets/content/properties/spawnpopulation/").ToList();
spawnDefaults = new Dictionary<string, object>();
foreach (var pop in spawnPopulations) {
spawnDefaults.Add(pop.name, pop.TargetDensity);
}
defaultSpawnPops = getFile($"{this.Title}_defaults");
defaultSpawnPops.Clear();
defaultSpawnPops.Set("Backup", spawnDefaults);
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>;
}
}
void OnServerInitialized() {
_startup = false;
if (!_loaded) OnTerrainInitialized();
GetPopulationNames();
if (_terrainReady && initialFillPopulations) NextTick(() => SpawnHandler.Instance.FillPopulations());
if (FillJobs.Count > 0) Puts($"Planned {FillJobs.Count} job(s) for spawn population");
FixBarricades();
LoadPopulationDefaults();
}
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() {
PopulationNames = new List<string>();
foreach (var pop in SpawnHandler.Instance.SpawnPopulations) PopulationNames.Add(pop.name);
}
void SpawnPopDefaults() {
var _handler = SpawnHandler.Instance;
var data = new Dictionary<string, object>();
SpawnPopulation.Clear();
foreach (var population in _handler.SpawnPopulations) {
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 = (Prefab<Spawnable>[])_PrefabsField.GetValue(population);
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(population.ResourceFolder)) _PrefabsField.SetValue(population, Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", population.ResourceFolder), GameManager.server, PrefabAttribute.server));
if (population.ResourceList != null && population.ResourceList.Length > 0) _PrefabsField.SetValue(population, Prefab.Load<Spawnable>(population.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server));
}
prefabs = (Prefab<Spawnable>[])_PrefabsField.GetValue(population);
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(population.name, populationdata);
}
SpawnPopulation = new Dictionary<string, object>(data);
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
Puts($"Created SpawnPopulation with '{data.Count}' populations.");
}
void SpawnPopLoad() {
var _handler = SpawnHandler.Instance;
Dictionary<string, object> data = new Dictionary<string, object>(SpawnPopulation);
if (data == null || data.Count() == 0) {
SpawnPopDefaults();
return;
}
List<string> populationnames = new List<string>();
foreach (var population in _handler.SpawnPopulations) populationnames.Add(population.name);
bool changed = false;
foreach (var population in data.ToList()) if (!populationnames.Contains(population.Key.ToString())) {
Puts($"Invalid SpawnPopulation removed: {population.Key}");
data.Remove(population.Key);
changed = true;
}
foreach (var population in _handler.SpawnPopulations) {
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 = (Prefab<Spawnable>[])_PrefabsField.GetValue(population);
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(population.ResourceFolder)) _PrefabsField.SetValue(population, Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", population.ResourceFolder), GameManager.server, PrefabAttribute.server));
if (population.ResourceList != null && population.ResourceList.Length > 0) _PrefabsField.SetValue(population, Prefab.Load<Spawnable>(population.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server));
}
prefabs = (Prefab<Spawnable>[])_PrefabsField.GetValue(population);
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;
}
}
SpawnPopulation = new Dictionary<string, object>(data);
if (changed) {
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
}
bool addedPrefabs = false;
foreach (var spawnPopulation in _handler.SpawnPopulations) {
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 = (Prefab<Spawnable>[])_PrefabsField.GetValue(spawnPopulation);
if (prefabs == null || prefabs.Length == 0) {
if (!string.IsNullOrEmpty(spawnPopulation.ResourceFolder)) _PrefabsField.SetValue(spawnPopulation, Prefab.Load<Spawnable>(string.Concat("assets/bundled/prefabs/autospawn/", spawnPopulation.ResourceFolder), GameManager.server, PrefabAttribute.server));
if (spawnPopulation.ResourceList != null && spawnPopulation.ResourceList.Length > 0) _PrefabsField.SetValue(spawnPopulation, Prefab.Load<Spawnable>(spawnPopulation.ResourceList.Select(x => x.resourcePath).ToArray(), GameManager.server, PrefabAttribute.server));
}
prefabs = (Prefab<Spawnable>[])_PrefabsField.GetValue(spawnPopulation);
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 );
spawnData.Add("spawnweights", prefabdata);
addedPrefabs = true;
}
if (enablePopulationPrefabs) {
Dictionary<string, object> prefabData = spawnData["spawnweights"] as Dictionary<string, object>;
var prefabs = new List<Prefab<Spawnable>>();
foreach (var prefab in prefabData) {
var gameObject = GameManager.server.FindPrefab(prefab.Key);
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, gameObject, component, GameManager.server, PrefabAttribute.server));
}
_PrefabsField.SetValue(spawnPopulation, prefabs.ToArray());
}
spawnPopulation.EnforcePopulationLimits = Convert.ToBoolean(spawnData["spawnlimits"]);
spawnPopulation.SpawnRate = Convert.ToSingle(spawnData["spawnrate"]);
_targetDensity.SetValue(spawnPopulation, Convert.ToSingle(spawnData["spawndensity"]));
if ((int)spawnData["spawnfillheartbeat"] > 0 ) {
FillPopulations.Add(spawnPopulation.name, (int)spawnData["spawnfillheartbeat"]);
heartbeatOn = true;
}
}
if (addedPrefabs) {
SpawnPopulation = new Dictionary<string, object>(data);
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
}
if (heartbeatOn) FillJobs = new Dictionary<string, int>(FillPopulations);
Puts($"Loaded '{data.Count}' SpawnPopulations");
}
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;
var _handler = SpawnHandler.Instance;
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"];
}
foreach (var method in group.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic )) {
if (!method.Name.Contains("Spawn") || method.GetParameters().Length > 0) continue;
spawnMethod = method;
break;
}
Action action = delegate {
spawnMethod.Invoke(group, null);
}
;
LocalClock spawnClock = (LocalClock)_spawnClock.GetValue(group);
spawnClock = new LocalClock();
spawnClock.Add(group.GetSpawnDelta(), group.GetSpawnVariance(), delegate {
spawnMethod.Invoke(group, null);
}
);
_spawnClock.SetValue(group, spawnClock);
group.SpawnRepeating();
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");
}
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 'all','population' or 'groups'");
return;
}
data = arg.Args[0];
if (data == "all") {
SpawnGroups.Clear();
SpawnPopulation.Clear();
Config["Spawn", "Groups"] = SpawnGroups;
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
SendReply(arg, "Did reset all data.Next server boot loads the game defaults");
return;
}
if (data == "population") {
SpawnPopulation.Clear();
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
SendReply(arg, "Did reset population data.Next server boot loads the game defaults");
return;
}
if (data == "groups") {
SpawnGroups.Clear();
Config["Spawn", "Groups"] = SpawnGroups;
Config.Save();
SendReply(arg, "Did reset groups data.Next server boot loads the game defaults");
return;
}
SendReply(arg, "You need to provide 'all','population' or 'groups'");
}
[ConsoleCommand("sc.tickinterval")]
void ccmdTickInterval(ConsoleSystem.Arg arg) {
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
var _handler = SpawnHandler.Instance;
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.StopCoroutine("SpawnTick");
_handler.TickInterval = currTickInterval;
_handler.StartCoroutine("SpawnTick");
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;
}
ServerMgr.Instance.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 _handler = SpawnHandler.Instance;
_handler.StopCoroutine("SpawnTick");
bool findany = false;
if (single) {
bool fullMatch = false;
SpawnDistribution[] spawndists = (SpawnDistribution[])_SpawnDistributions.GetValue(_handler);
for (int i = 0; i < _handler.SpawnPopulations.Length; i++) {
if (!(_handler.SpawnPopulations[i] == null)) {
if ((_handler.SpawnPopulations[i].name.StartsWith(populationname) || _handler.SpawnPopulations[i].name.EndsWith(populationname)) && !fullMatch) {
if (_handler.SpawnPopulations[i].name == populationname) fullMatch = true;
int oldCount = _handler.GetCurrentCount(_handler.SpawnPopulations[i]);
_handler.SpawnInitial(_handler.SpawnPopulations[i], spawndists[i]);
if (arg.Args.Length > 1 && arg.Args[1] == "auto") if (logJobsToConsole) SendReply(arg, $"Timed population of '{_handler.SpawnPopulations[i].name}' | Old: {oldCount} | New: {_handler.GetCurrentCount(_handler.SpawnPopulations[i])}");
if (arg.Args.Length == 1) SendReply(arg, $"Forced population of '{_handler.SpawnPopulations[i].name}' | Old: {oldCount} | New: {_handler.GetCurrentCount(_handler.SpawnPopulations[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(_handler);
for (int i = 0; i < _handler.SpawnPopulations.Length; i++) {
if (!(_handler.SpawnPopulations[i] == null)) {
oldCount += _handler.GetCurrentCount(_handler.SpawnPopulations[i]);
_handler.SpawnInitial(_handler.SpawnPopulations[i], spawndists[i]);
newCount += _handler.GetCurrentCount(_handler.SpawnPopulations[i]);
yield return new WaitForEndOfFrame();
}
}
SendReply(arg, $"Filled SpawnPopulations | Old: {oldCount} | New: {newCount}");
}
_handler.StartCoroutine("SpawnTick");
runningSpawnFill = false;
yield return null;
}
[ConsoleCommand("sc.raisedensity")]
void ccmdSpawnRaiseDensity(ConsoleSystem.Arg arg) {
var _handler = SpawnHandler.Instance;
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}'");
ServerMgr.Instance.StartCoroutine(SpawnRaiseDensity(percentage, populationname, raiseAll, arg));
}
IEnumerator SpawnRaiseDensity(float percentage, string populationname, bool raiseAll, ConsoleSystem.Arg arg) {
runningSpawnRaiseDensity = true;
var _handler = SpawnHandler.Instance;
_handler.StopCoroutine("SpawnTick");
bool findany = false;
if (!raiseAll) {
bool fullMatch = false;
for (int i = 0; i < _handler.SpawnPopulations.Length; i++) {
if (!(_handler.SpawnPopulations[i] == null)) {
if ((_handler.SpawnPopulations[i].name.StartsWith(populationname) || _handler.SpawnPopulations[i].name.EndsWith(populationname)) && !fullMatch) {
if (_handler.SpawnPopulations[i].name == populationname) fullMatch = true;
float oldDensity = _handler.SpawnPopulations[i].TargetDensity;
float increaseDensity = 0f;
if (oldDensity > 0f) increaseDensity = _handler.SpawnPopulations[i].TargetDensity / 100 * percentage;
else {
object backupDensity = null;
if (spawnDefaults.TryGetValue(_handler.SpawnPopulations[i].name, out backupDensity)) increaseDensity = Convert.ToSingle(backupDensity) / 100 * percentage;
else increaseDensity = _handler.SpawnPopulations[i].TargetDensity / 100 * percentage;
}
float newDensity = (float)Math.Round((double)(oldDensity+increaseDensity));
_targetDensity.SetValue(_handler.SpawnPopulations[i], newDensity);
(SpawnPopulation[_handler.SpawnPopulations[i].name] as Dictionary<string, object>)["spawndensity"] = _handler.SpawnPopulations[i].TargetDensity;
SendReply(arg, $"Raised density of '{_handler.SpawnPopulations[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"] = SpawnPopulation;
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 < _handler.SpawnPopulations.Length; i++) {
if (!(_handler.SpawnPopulations[i] == null)) {
float oldDensity = _handler.SpawnPopulations[i].TargetDensity;
float increaseDensity = _handler.SpawnPopulations[i].TargetDensity / 100 * percentage;
float newDensity = (float)Math.Round((double)(oldDensity+increaseDensity));
_targetDensity.SetValue(_handler.SpawnPopulations[i], newDensity);
(SpawnPopulation[_handler.SpawnPopulations[i].name] as Dictionary<string, object>)["spawndensity"] = _handler.SpawnPopulations[i].TargetDensity;
}
}
SendReply(arg, "New raised density's set and saved.");
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
arg.Args = null;
NextTick(() => ccmdSpawnFill(arg));
}
_handler.StartCoroutine("SpawnTick");
runningSpawnRaiseDensity = false;
yield return null;
}
[ConsoleCommand("sc.lowerdensity")]
void ccmdSpawnLowerDensity(ConsoleSystem.Arg arg) {
var _handler = SpawnHandler.Instance;
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}'");
ServerMgr.Instance.StartCoroutine(SpawnLowerDensity(percentage, populationname, lowerAll, arg));
}
IEnumerator SpawnLowerDensity(float percentage, string populationname, bool lowerAll, ConsoleSystem.Arg arg) {
runningSpawnLowerDensity = true;
var _handler = SpawnHandler.Instance;
_handler.StopCoroutine("SpawnTick");
bool findany = false;
if (!lowerAll) {
bool fullMatch = false;
for (int i = 0; i < _handler.SpawnPopulations.Length; i++) {
if (!(_handler.SpawnPopulations[i] == null)) {
if ((_handler.SpawnPopulations[i].name.StartsWith(populationname) || _handler.SpawnPopulations[i].name.EndsWith(populationname)) && !fullMatch) {
if (_handler.SpawnPopulations[i].name == populationname) fullMatch = true;
float oldDensity = _handler.SpawnPopulations[i].TargetDensity;
float decreaseDensity = _handler.SpawnPopulations[i].TargetDensity / 100 * percentage;
float newDensity = -1f;
if (percentage == 100f) newDensity = 0f;
else newDensity = (float)Math.Round((double)(oldDensity-decreaseDensity));
_targetDensity.SetValue(_handler.SpawnPopulations[i], newDensity);
(SpawnPopulation[_handler.SpawnPopulations[i].name] as Dictionary<string, object>)["spawndensity"] = _handler.SpawnPopulations[i].TargetDensity;
SendReply(arg, $"Lowered density of '{_handler.SpawnPopulations[i].name}' | Old: {oldDensity} | New: {newDensity}");
findany = true;
}
}
}
if (!findany) SendReply(arg, $"SpawnPopulation '{populationname}' not found");
else {
SendReply(arg, $"New lowered density's set and saved.");
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
NextTick(() => ccmdEnforceLimits(arg));
}
}
else {
SendReply(arg, $"Lowering all SpawnPopulation density's by '{percentage}' percent");
for (int i = 0; i < _handler.SpawnPopulations.Length; i++) {
if (!(_handler.SpawnPopulations[i] == null)) {
float oldDensity = _handler.SpawnPopulations[i].TargetDensity;
float decreaseDensity = _handler.SpawnPopulations[i].TargetDensity / 100 * percentage;
float newDensity = -1f;
if (percentage == 100f) newDensity = 0f;
else newDensity = (float)Math.Round((double)(oldDensity-decreaseDensity));
_targetDensity.SetValue(_handler.SpawnPopulations[i], newDensity);
(SpawnPopulation[_handler.SpawnPopulations[i].name] as Dictionary<string, object>)["spawndensity"] = _handler.SpawnPopulations[i].TargetDensity;
}
}
SendReply(arg, $"New lowered density's set and saved");
Config["Spawn", "Population"] = SpawnPopulation;
Config.Save();
NextTick(() => ccmdEnforceLimits(arg));
}
_handler.StartCoroutine("SpawnTick");
runningSpawnLowerDensity = false;
yield return null;
}
[ConsoleCommand("sc.kill")]
void ccmdSpawnKill(ConsoleSystem.Arg arg) {
var _handler = SpawnHandler.Instance;
if(arg.Connection != null && arg.Connection.authLevel < 2) return;
string populationname = string.Empty;
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, $"You need to provide a valid SpawnPopulation name");
return;
}
populationname = arg.Args[0];
bool killAll = false;
if (populationname == "all") killAll = true;
SendReply(arg, $"Calling kill for '{populationname}'");
ServerMgr.Instance.StartCoroutine(SpawnKill(populationname, killAll, arg));
}
IEnumerator SpawnKill(string populationname, bool killAll, ConsoleSystem.Arg arg) {
runningSpawnKill = true;
var _handler = SpawnHandler.Instance;
_handler.StopCoroutine("SpawnTick");
bool findany = false;
SpawnPopulation[] spawnpops = _handler.SpawnPopulations.ToArray();
int countDeleted = 0;
bool fullMatch = false;
bool junkPiles = false;
for (int i = 0; i < spawnpops.Length; i++) {
if (!(spawnpops[i] == null)) {
if ((spawnpops[i].name.StartsWith(populationname) || (spawnpops[i].name.EndsWith(populationname)) && !fullMatch) || killAll) {
if (spawnpops[i].name == populationname) fullMatch = true;
Spawnable[] array = _handler.FindAll(spawnpops[i]);
int count = array.Length;
foreach (Spawnable current in array) {
BaseEntity baseEntity = current.gameObject.ToBaseEntity();
if (baseEntity.IsValid()) {
if (baseEntity is JunkPile) {
(baseEntity as JunkPile).SinkAndDestroy();
junkPiles = true;
}
else baseEntity.Kill(BaseNetworkable.DestroyMode.None);
countDeleted++;
}
else {
GameManager.Destroy(current.gameObject, 0f);
countDeleted++;
}
}
if (!killAll) {
if (junkPiles) SendReply(arg, $"Forced deletion of '{spawnpops[i].name}' | Old: {count} | New: {_handler.GetCurrentCount
// ... [Code preview truncated due to length. Full source in attached file] ...