C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using HarmonyLib;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using VLB;
namespace Oxide.Plugins
{
[Info("Night Lantern", "k1lly0u", "2.1.1")]
[Description("Automatically turns ON and OFF lanterns after sunset and sunrise")]
class NightLantern : RustPlugin
{
#region Fields
[PluginReference]
private Plugin NoFuelRequirements;
private static Hash<ulong, Dictionary<EntityType, bool>> _toggleList = new Hash<ulong, Dictionary<EntityType, bool>>();
private readonly HashSet<LightController> _lightControllers = new HashSet<LightController>();
private static readonly Hash<BaseOven, LightController> OvenControllers = new Hash<BaseOven, LightController>();
private bool _lightsOn = false;
private bool _globalToggle = true;
private Timer _timeCheck;
private static Func<string, ulong, object> _ignoreFuelConsumptionFunction;
#endregion Fields
#region Oxide Hooks
private void Init()
{
permission.RegisterPermission("nightlantern.global", this);
foreach (EntityType type in (EntityType[])Enum.GetValues(typeof(EntityType)))
{
if (type == EntityType.CeilingLight)
continue;
permission.RegisterPermission($"nightlantern.{type}", this);
}
_ignoreFuelConsumptionFunction = NoFuelRequirementsIgnoreFuelConsumption;
LoadData();
Unsubscribe(nameof(OnEntitySpawned));
Unsubscribe(nameof(OnEntityKill));
}
protected override void LoadDefaultMessages()
=> lang.RegisterMessages(_messages, this);
private void OnServerInitialized()
{
Subscribe(nameof(OnEntitySpawned));
Subscribe(nameof(OnEntityKill));
ServerMgr.Instance.StartCoroutine(CreateAllLights(BaseNetworkable.serverEntities.Where(x => x is BaseOven || x is SearchLight)));
}
private object OnFuelConsume(BaseOven baseOven, Item fuel, ItemModBurnable burnable)
{
if (!baseOven || baseOven.IsDestroyed)
return null;
if (!OvenControllers.TryGetValue(baseOven, out LightController lightController) || !lightController)
return null;
return lightController.OnConsumeFuel();
}
private void OnOvenToggle(BaseOven baseOven, BasePlayer player)
{
if (!baseOven || baseOven.IsDestroyed)
return;
if (baseOven.needsBuildingPrivilegeToUse && !player.CanBuild())
return;
if (!OvenControllers.TryGetValue(baseOven, out LightController lightController) || !lightController)
return;
lightController.OnOvenToggled();
}
private void OnEntitySpawned(BaseEntity entity)
=> InitializeLightController(entity);
private void OnEntityKill(BaseNetworkable entity)
{
LightController lightController = entity.GetComponent<LightController>();
if (lightController)
{
_lightControllers.Remove(lightController);
UnityEngine.Object.Destroy(lightController);
}
}
private void OnServerSave() => SaveData();
private void Unload()
{
foreach (LightController lightController in _lightControllers)
{
lightController.ToggleLight(false);
UnityEngine.Object.DestroyImmediate(lightController);
}
_timeCheck?.Destroy();
_lightControllers.Clear();
_configData = null;
_toggleList = null;
}
#endregion Oxide Hooks
#region Functions
private IEnumerator CreateAllLights(IEnumerable<BaseNetworkable> entities)
{
foreach (BaseNetworkable baseNetworkable in entities)
{
yield return new WaitForSeconds(UnityEngine.Random.Range(0.1f, 0.25f));
if (!baseNetworkable || baseNetworkable.IsDestroyed)
continue;
InitializeLightController(baseNetworkable as BaseEntity);
}
CheckCurrentTime();
}
private void InitializeLightController(BaseEntity entity)
{
if (!entity || entity.IsDestroyed)
return;
EntityType entityType = StringToType(entity.ShortPrefabName);
if (entityType == EntityType.None || !_configData.Types[entityType].Enabled)
return;
_lightControllers.Add(entity.GetOrAddComponent<LightController>());
}
private void CheckCurrentTime()
{
if (_globalToggle)
{
float time = TOD_Sky.Instance.Cycle.Hour;
if (time >= _configData.Sunset || (time >= 0 && time < _configData.Sunrise))
{
if (!_lightsOn)
{
ServerMgr.Instance.StartCoroutine(ToggleAllLights(_lightControllers, true));
_lightsOn = true;
}
}
else if (time >= _configData.Sunrise && time < _configData.Sunset)
{
if (_lightsOn)
{
ServerMgr.Instance.StartCoroutine(ToggleAllLights(_lightControllers, false));
_lightsOn = false;
}
}
}
_timeCheck = timer.Once(20, CheckCurrentTime);
}
private static IEnumerator ToggleAllLights(IEnumerable<LightController> lights, bool status)
{
foreach (LightController lightController in lights)
{
yield return new WaitForSeconds(UnityEngine.Random.Range(0.1f, 0.25f));
if (lightController)
lightController.ToggleLight(status);
}
}
private static EntityType StringToType(string name)
{
return name switch
{
"campfire" => EntityType.Campfire,
"skull_fire_pit" => EntityType.Firepit,
"fireplace.deployed" => EntityType.Fireplace,
"furnace" => EntityType.Furnace,
"furnace.large" => EntityType.LargeFurnace,
"lantern.deployed" => EntityType.Lanterns,
"jackolantern.angry" => EntityType.JackOLantern,
"jackolantern.happy" => EntityType.JackOLantern,
"tunalight.deployed" => EntityType.TunaLight,
"searchlight.deployed" => EntityType.Searchlight,
"bbq.deployed" => EntityType.BBQ,
"refinery_small_deployed" => EntityType.Refinery,
"cursedcauldron.deployed" => EntityType.CursedCauldren,
"chineselantern.deployed" => EntityType.ChineseLantern,
"chineselantern_white.deployed" => EntityType.ChineseLantern,
_ => EntityType.None
};
}
private static EntityType ParseType(string type)
{
try
{
return (EntityType)Enum.Parse(typeof(EntityType), type, true);
}
catch
{
return EntityType.None;
}
}
private static bool ConsumeTypeEnabled(ulong playerId, EntityType entityType)
{
if (_toggleList.TryGetValue(playerId, out Dictionary<EntityType, bool> userPreferences))
return userPreferences[entityType];
return _configData.Types[entityType].Enabled;
}
private object NoFuelRequirementsIgnoreFuelConsumption(string shortname, ulong playerId)
=> NoFuelRequirements?.Call("IgnoreFuelConsumption", shortname, playerId);
#endregion Functions
#region Component
private class LightController : MonoBehaviour
{
private BaseEntity _entity;
private ConfigData.LightSettings _config;
private bool _isSearchlight;
private bool _ignoreFuelConsumption;
private bool _automaticallyToggled;
public EntityType entityType;
public bool ShouldIgnoreFuelConsumption
{
get
{
if (_config.ConsumeFuelWhenToggled && !_automaticallyToggled)
return false;
return _ignoreFuelConsumption || !_config.ConsumeFuel;
}
}
private void Awake()
{
_entity = GetComponent<BaseEntity>();
entityType = StringToType(_entity.ShortPrefabName);
_config = _configData.Types[entityType];
_isSearchlight = _entity is SearchLight;
object success = _ignoreFuelConsumptionFunction(entityType.ToString(), _entity.OwnerID);
if (success != null)
_ignoreFuelConsumption = true;
}
private void OnEnable()
{
if (_entity is BaseOven baseOven)
OvenControllers[baseOven] = this;
}
private void OnDisable()
{
if (_entity is BaseOven baseOven)
OvenControllers.Remove(baseOven);
}
public void ToggleLight(bool status)
{
if (_config.Owner && !ConsumeTypeEnabled(_entity.OwnerID, entityType))
status = false;
object success = Interface.CallHook("OnNightLanternToggle", _entity, status);
if (success != null)
return;
if (_isSearchlight)
{
SearchLight searchLight = _entity as SearchLight;
if (searchLight)
searchLight.SetFlag(BaseEntity.Flags.On, status);
}
else
{
BaseOven baseOven = _entity as BaseOven;
if (baseOven)
{
if (_config.ConsumeFuel)
{
if (status)
baseOven.StartCooking();
else baseOven.StopCooking();
}
else
{
if (baseOven.IsOn() != status)
{
_automaticallyToggled = true;
if (_config.Cook)
{
if (status)
{
baseOven.inventory.temperature = baseOven.cookingTemperature;
baseOven.UpdateAttachmentTemperature();
baseOven.InvokeRepeating(baseOven.Cook, 0.5f, 0.5f);
baseOven.SetFlag(BaseEntity.Flags.On, true, false, true);
Interface.CallHook("OnOvenStarted", this);
}
else
{
if (_automaticallyToggled)
{
baseOven.UpdateAttachmentTemperature();
if (baseOven.inventory != null)
{
baseOven.inventory.temperature = 15f;
foreach (Item item in baseOven.inventory.itemList)
{
if (item.HasFlag(global::Item.Flag.OnFire))
{
item.SetFlag(global::Item.Flag.OnFire, false);
item.MarkDirty();
}
else if (item.HasFlag(global::Item.Flag.Cooking))
{
item.SetFlag(global::Item.Flag.Cooking, false);
item.MarkDirty();
}
}
}
baseOven.CancelInvoke(baseOven.Cook);
}
}
}
baseOven.SetFlag(BaseEntity.Flags.On, status);
}
}
}
}
_entity.SendNetworkUpdate();
}
public void OnOvenToggled() => _automaticallyToggled = false;
public object OnConsumeFuel() => ShouldIgnoreFuelConsumption ? true : (object)null;
public bool IsOwner(ulong playerId) => _entity.OwnerID == playerId;
}
[AutoPatch]
[HarmonyPatch(typeof(BaseOven))]
[HarmonyPatch(nameof(BaseOven.CanRunWithNoFuel), MethodType.Getter)]
private static class BaseOven_CanRunWithNoFuelPatch
{
[HarmonyPrefix]
private static bool Prefix(BaseOven __instance, ref bool __result)
{
if (!OvenControllers.TryGetValue(__instance, out LightController lightController))
return false;
if (lightController && lightController.ShouldIgnoreFuelConsumption)
{
__result = true;
return false;
}
return true;
}
}
#endregion Component
#region Commands
private StringBuilder _stringBuilder = new StringBuilder();
[ChatCommand("lantern")]
private void cmdLantern(BasePlayer player, string command, string[] args)
{
if (args.Length == 0)
{
_stringBuilder.Clear();
_stringBuilder.AppendLine(GetMessage("global.title", player.userID));
if (!_toggleList.TryGetValue(player.userID, out Dictionary<EntityType, bool> userPreferences))
userPreferences = _configData.Types.ToDictionary(x => x.Key, y => y.Value.Enabled);
bool canToggle = false;
foreach (KeyValuePair<EntityType, ConfigData.LightSettings> lightType in _configData.Types)
{
if (lightType.Key == EntityType.CeilingLight)
continue;
if (lightType.Value.Owner)
{
if (lightType.Value.Permission && !permission.UserHasPermission(player.UserIDString, $"nightlantern.{lightType.Key}"))
continue;
_stringBuilder.AppendLine(string.Format(GetMessage("user.type", player.userID), lightType.Key, userPreferences[lightType.Key] ? GetMessage("user.enabled", player.userID) : GetMessage("user.disabled", player.userID)));
canToggle = true;
}
}
if (canToggle)
_stringBuilder.AppendLine(GetMessage("user.toggle.command", player.userID));
player.ChatMessage(_stringBuilder.ToString());
_stringBuilder.Clear();
if (!permission.UserHasPermission(player.UserIDString, "nightlantern.global"))
return;
_stringBuilder.AppendLine(string.Format(GetMessage("global.toggle", player.userID), _globalToggle ? GetMessage("user.enabled", player.userID) : GetMessage("user.disabled", player.userID)));
_stringBuilder.AppendLine(GetMessage("global.toggle.command", player.userID));
player.ChatMessage(_stringBuilder.ToString());
_stringBuilder.Clear();
return;
}
if (args[0].ToLower() == "global" && permission.UserHasPermission(player.UserIDString, "nightlantern.global"))
{
_globalToggle = !_globalToggle;
ServerMgr.Instance.StartCoroutine(ToggleAllLights(_lightControllers, _globalToggle));
player.ChatMessage(string.Format(GetMessage("global.toggle", player.userID), _globalToggle ? GetMessage("user.enabled", player.userID) : GetMessage("user.disabled", player.userID)));
}
else
{
EntityType entityType = ParseType(args[0]);
if ((entityType == EntityType.None || entityType == EntityType.CeilingLight) || !permission.UserHasPermission(player.UserIDString, $"nightlantern.{entityType}"))
{
player.ChatMessage(string.Format(GetMessage("toggle.invalid", player.userID), entityType));
return;
}
if (!_toggleList.ContainsKey(player.userID))
_toggleList.Add(player.userID, _configData.Types.ToDictionary(x => x.Key, y => y.Value.Enabled));
_toggleList[player.userID][entityType] = !_toggleList[player.userID][entityType];
IEnumerable<LightController> ownedLights = _lightControllers.Where(x => x.IsOwner(player.userID) && x.entityType == entityType).ToList();
if (ownedLights.Any())
ServerMgr.Instance.StartCoroutine(ToggleAllLights(ownedLights, _toggleList[player.userID][entityType]));
player.ChatMessage(string.Format(GetMessage("user.type", player.userID), entityType, _toggleList[player.userID][entityType] ? GetMessage("user.enabled", player.userID) : GetMessage("user.disabled", player.userID)));
}
}
#endregion Commands
#region Config
private enum EntityType { BBQ, Campfire, CeilingLight, ChineseLantern, CursedCauldren, Firepit, Fireplace, Furnace, LargeFurnace, Lanterns, JackOLantern, TunaLight, Searchlight, Refinery, None }
private static ConfigData _configData;
class ConfigData
{
[JsonProperty(PropertyName = "Light Settings")]
public Dictionary<EntityType, LightSettings> Types { get; set; }
[JsonProperty(PropertyName = "Time autolights are disabled")]
public float Sunrise { get; set; }
[JsonProperty(PropertyName = "Time autolights are enabled")]
public float Sunset { get; set; }
public class LightSettings
{
[JsonProperty(PropertyName = "This type is enabled")]
public bool Enabled { get; set; }
[JsonProperty(PropertyName = "This type consumes fuel")]
public bool ConsumeFuel { get; set; }
[JsonProperty(PropertyName = "This type consumes fuel when toggled by a player")]
public bool ConsumeFuelWhenToggled { get; set; }
[JsonProperty(PropertyName = "This type starts cooking items when toggled by plugin")]
public bool Cook { get; set; }
[JsonProperty(PropertyName = "This type can be toggled by the owner")]
public bool Owner { get; set; }
[JsonProperty(PropertyName = "This type requires permission to be toggled by the owner")]
public bool Permission { get; set; }
}
public VersionNumber Version { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
_configData = Config.ReadObject<ConfigData>();
if (_configData.Version < Version)
UpdateConfigValues();
Config.WriteObject(_configData, true);
}
protected override void LoadDefaultConfig() => _configData = GetBaseConfig();
private ConfigData GetBaseConfig()
{
return new ConfigData
{
Types = new Dictionary<EntityType, ConfigData.LightSettings>
{
[EntityType.BBQ] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Campfire] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Firepit] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Fireplace] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Furnace] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = false
},
[EntityType.JackOLantern] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Lanterns] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.LargeFurnace] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Searchlight] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.TunaLight] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.Refinery] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.CursedCauldren] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
},
[EntityType.ChineseLantern] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Permission = true,
Owner = true
}
},
Sunrise = 7.5f,
Sunset = 18.5f,
Version = Version
};
}
protected override void SaveConfig() => Config.WriteObject(_configData, true);
private void UpdateConfigValues()
{
PrintWarning("Config update detected! Updating config values...");
ConfigData baseConfig = GetBaseConfig();
if (_configData.Version < new VersionNumber(2, 0, 9))
_configData = baseConfig;
foreach (EntityType entityType in (EntityType[])Enum.GetValues(typeof(EntityType)))
{
if (!_configData.Types.ContainsKey(entityType))
{
_configData.Types[entityType] = new ConfigData.LightSettings
{
ConsumeFuel = true,
Enabled = true,
Cook = false,
Permission = true,
Owner = true
};
}
}
_configData.Version = Version;
PrintWarning("Config update completed!");
}
#endregion Config
#region Data Management
private void SaveData() => Interface.Oxide.DataFileSystem.GetFile("nightlantern_data").WriteObject(_toggleList);
private void LoadData()
{
try
{
_toggleList = Interface.Oxide.DataFileSystem.GetFile("nightlantern_data")?.ReadObject<Hash<ulong, Dictionary<EntityType, bool>>>() ?? new Hash<ulong, Dictionary<EntityType, bool>>();
}
catch
{
_toggleList = new Hash<ulong, Dictionary<EntityType, bool>>();
}
}
#endregion Data Management
#region Localization
private string GetMessage(string key, ulong playerId = 0U) => lang.GetMessage(key, this, playerId == 0U ? null : playerId.ToString());
private readonly Dictionary<string, string> _messages = new Dictionary<string, string>
{
["global.title"] = "<color=#FFA500>Night Lantern</color>",
["global.toggle"] = "Auto lights are {0} server wide",
["global.toggle.command"] = "You can toggle auto lights globally by typing '<color=#FFA500>/lantern global</color>'",
["user.disable"] = "You have disabled auto lights that you own of the type {0}",
["user.enable"] = "You have enabled auto lights that you own of the type {0}",
["user.type"] = "{0} : {1}",
["user.enabled"] = "<color=#8ee700>enabled</color>",
["user.disabled"] = "<color=#e90000>disabled</color>",
["user.toggle.command"] = "You can toggle the various types by typing '<color=#FFA500>/lantern <light type></color>'",
["toggle.invalid"] = "{0} is an invalid option!"
};
#endregion Localization
}
}