C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Rust;
using System;
using Oxide.Core;
using UnityEngine;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using System.Collections;
using System.Diagnostics;
using Oxide.Game.Rust.Cui;
using Random = System.Random;
using System.Collections.Generic;
using Oxide.Core.Libraries.Covalence;
using System.Runtime.CompilerServices;
namespace Oxide.Plugins {
[Info("Sync Pipes", "Joe 90", "0.9.32")]
[Description("Allows players to transfer items between containers.All pipes from a container are used synchronously to enable advanced sorting and splitting.")]
partial class SyncPipes : RustPlugin {
#region Initialization
/// <summary>
/// The instance of syncPipes on the server to allow child classes to access it
/// </summary>
private static SyncPipes Instance;
private const string ToolCupboardPrefab = "cupboard.tool.deployed";
#pragma warning disable CS0649
// Reference to the Furnace Splitter plugin https://umod.org/plugins/furnace-splitter
// Disabled as 01/09/2022 rust update causes items to vanish
Plugin FurnaceSplitter = null;
// Refernce to the Quick Smelt plugin https://umod.org/plugins/quick-smelt
[PluginReference]
Plugin QuickSmelt;
#pragma warning restore CS0649
/// <summary>
/// Hook: Initializes syncPipes when the server starts to load it
/// </summary>
void Init() {
Instance = this;
_config = SyncPipesConfig.Load();
Commands.InitializeChat();
permission.RegisterPermission($"{Name}.user", this);
permission.RegisterPermission($"{Name}.admin", this);
InstanceConfig.RegisterPermissions();
#region static data declarations
_chatCommands = new Dictionary<Enum, bool> {
{
Oxide.Plugins.SyncPipes.Chat.Commands, true
}
, {
Oxide.Plugins.SyncPipes.Overlay.CancelPipeCreationFromChat, true
}
, {
Oxide.Plugins.SyncPipes.Overlay.CancelCopy, true
}
, {
Oxide.Plugins.SyncPipes.Overlay.CancelRemove, true
}
, {
Oxide.Plugins.SyncPipes.PipeMenu.HelpLabel.FlowBar, true
}
,
}
;
_bindingCommands = new Dictionary<Enum, bool> {
{
Oxide.Plugins.SyncPipes.Chat.PlacingBindingHint, true
}
, {
Oxide.Plugins.SyncPipes.Overlay.CancelPipeCreationFromBind, true
}
,
}
;
_messageTypes = new Dictionary<Enum, MessageType> {
{
Oxide.Plugins.SyncPipes.Overlay.AlreadyConnected, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.TooFar, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.TooFarTC, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.TooClose, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.NoPrivilegeToCreate, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.MonumentDenied, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.CantConnectTwoToolcuboards, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.BlacklistedContainer, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.NoPrivilegeToEdit, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.PipeLimitReached, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.UpgradeLimitReached, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.HitFirstContainer, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.HitSecondContainer, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.HitToName, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.HitToClearName, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.HitToGetBuildingId, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.HitToSetBuildingId, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.CannotNameContainer, MessageType.Warning
}
, {
Oxide.Plugins.SyncPipes.Overlay.CopyFromPipe, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.CopyToPipe, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Overlay.RemovePipe, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Pipe.Status.Pending, MessageType.Info
}
, {
Oxide.Plugins.SyncPipes.Pipe.Status.Success, MessageType.Success
}
, {
Oxide.Plugins.SyncPipes.Pipe.Status.SourceError, MessageType.Error
}
, {
Oxide.Plugins.SyncPipes.Pipe.Status.DestinationError, MessageType.Error
}
, {
Oxide.Plugins.SyncPipes.Pipe.Status.IdGenerationFailed, MessageType.Error
}
,
}
;
_storageDetails = new Dictionary<Storage, StorageData> {
{
Storage.Default, new StorageData("unknown.container", "https://i.imgur.com/cayN7SQ.png", new Vector3(0f, 0f, 0f), false)
}
, {
Storage.PumpJackCrudeOutput, new StorageData("crudeoutput", "c/c9/Pump_Jack_icon.png", new Vector3(0f, 0f, -0.5f), true)
}
, {
Storage.Fireplace, new StorageData("fireplace.deployed", "https://static.wikia.nocookie.net/rust_gamepedia/images/c/c2/Stone_Fireplace.png/revision/latest/scale-to-width-down/{0}", new Vector3(0f, -1.3f, 0f), false)
}
, {
Storage.ResearchTable, new StorageData("researchtable_deployed", "2/21/Research_Table_icon.png", new Vector3(0.8f, -0.5f, -0.3f), true)
}
, {
Storage.VendingMachine, new StorageData("vendingmachine.deployed", "5/5c/Vending_Machine_icon.png", new Vector3(0f, -0.5f, 0f), true)
}
, {
Storage.QuarryFuelInput, new StorageData("fuelstorage", "b/b8/Mining_Quarry_icon.png", new Vector3(-0.5f, 0f, -0.4f), true)
}
, {
Storage.SmallPlanterBox, new StorageData("planter.small.deployed", "a/a7/Small_Planter_Box_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.JackOLanternHappy, new StorageData("jackolantern.happy", "9/92/Jack_O_Lantern_Happy_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.DropBox, new StorageData("dropbox.deployed", "4/46/Drop_Box_icon.png", new Vector3(0f, 0.4f, 0.3f), true)
}
, {
Storage.MiningQuarry, new StorageData("mining_quarry", "b/b8/Mining_Quarry_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.SuperStocking, new StorageData("stocking_large_deployed", "6/6a/SUPER_Stocking_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.QuarryHopperOutput, new StorageData("hopperoutput", "b/b8/Mining_Quarry_icon.png", new Vector3(0f, -0.6f, -0.3f), true)
}
, {
Storage.SmallOilRefinery, new StorageData("refinery_small_deployed", "a/ac/Small_Oil_Refinery_icon.png", new Vector3(-0.3f, -0.2f, -0.1f), true)
}
, {
Storage.LargePlanterBox, new StorageData("planter.large.deployed", "3/35/Large_Planter_Box_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.ShotgunTrap, new StorageData("guntrap.deployed", "6/6c/Shotgun_Trap_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.LargeFurnace, new StorageData("furnace.large", "e/ee/Large_Furnace_icon.png", new Vector3(0f, -1.5f, 0f), true)
}
, {
Storage.WoodStorageBox, new StorageData("woodbox_deployed", "f/ff/Wood_Storage_Box_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.PumpJack, new StorageData("mining.pumpjack", "c/c9/Pump_Jack_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.Recycler, new StorageData("recycler_static", "e/ef/Recycler_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.Fridge, new StorageData("fridge.deployed", "8/88/Fridge_icon.png", new Vector3(0f, -0.5f, 0f), true)
}
, {
Storage.JackOLanternAngry, new StorageData("jackolantern.angry", "9/96/Jack_O_Lantern_Angry_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.SkullFirePit, new StorageData("skull_fire_pit", "3/32/Skull_Fire_Pit_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.Composter, new StorageData("composter", "https://i.imgur.com/qpA7I8P.png", new Vector3(0f, 0f, 0f), false)
}
, {
Storage.CampFire, new StorageData("campfire", "3/35/Camp_Fire_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.LargeWoodBox, new StorageData("box.wooden.large", "b/b2/Large_Wood_Box_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.Barbeque, new StorageData("bbq.deployed", "f/f8/Barbeque_icon.png", new Vector3(-0.2f, -0.2f, 0f), true)
}
, {
Storage.ToolCupboard, new StorageData("cupboard.tool.deployed", "5/57/Tool_Cupboard_icon.png", new Vector3(0f, -0.5f, 0f), true)
}
, {
Storage.SmallStash, new StorageData("small_stash_deployed", "5/53/Small_Stash_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.Mailbox, new StorageData("mailbox.deployed", "1/17/Mail_Box_icon.png", new Vector3(0f, 0f, -0.15f), true)
}
, {
Storage.Furnace, new StorageData("furnace", "e/e3/Furnace_icon.png", new Vector3(0f, -0.5f, 0f), true)
}
, {
Storage.SurvivalFishTrap, new StorageData("survivalfishtrap.deployed", "9/9d/Survival_Fish_Trap_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.SmallStocking, new StorageData("stocking_small_deployed", "9/97/Small_Stocking_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.AutoTurret, new StorageData("autoturret_deployed", "f/f9/Auto_Turret_icon.png", new Vector3(0f, -0.58f, 0f), true)
}
, {
Storage.RepairBench, new StorageData("repairbench_deployed", "3/3b/Repair_Bench_icon.png", new Vector3(0f, 0f, 0f), true)
}
, {
Storage.FlameTurret, new StorageData("flameturret.deployed", "f/f9/Flame_Turret_icon.png", new Vector3(0f, -0.3f, 0.1f), true)
}
, {
Storage.PumpJackFuelInput, new StorageData("fuelstorage", "c/c9/Pump_Jack_icon.png", new Vector3(-0.5f, 0.1f, -0.3f), true)
}
,
}
;
#endregion
}
/// <summary>
/// Hook: Cleans up syncPipes when the server unloads it
/// </summary>
void Unload() {
Instance.Puts("SyncPipes is unloading...");
DataStore.OnePointOne.Save(false);
Puts("Unloading All Pipes");
Pipe.Cleanup();
ContainerManager.Cleanup();
PlayerHelper.Cleanup();
ExperimentalUnload();
Instance.Puts("SyncPipes unloaded");
}
partial void ExperimentalUnload();
#endregion
#region Commands
/// <summary>
/// Contains all the commands used by syncPipes
/// Various stubs are included in the main SyncPipes class as this is needed by oxide
/// </summary>
static class Commands {
/// <summary>
/// Adds all the chat commands to Oxide
/// </summary>
public static void InitializeChat() {
Add("", nameof(CommandArgs));
Add("help", nameof(CommandHelp));
Add("copy", nameof(CommandCopy));
Add("remove", nameof(CommandRemove));
Add("stats", nameof(CommandStats));
Add("name", nameof(CommandName));
}
/// <summary>
/// Helper to simplify adding the commands to Oxide./// Adds the standard command prefix characters (from the config) to each command
/// </summary>
/// <param name="commandSuffix">The name of the command to be suffixed to the chat command charactes</param>
/// <param name="callback">The name of the method to be called by this command</param>
private static void Add(string commandSuffix, string callback) => Instance.AddCovalenceCommand($"{InstanceConfig.CommandPrefix}{commandSuffix}", callback);
/// <summary>
/// Default args based command
/// </summary>
/// <param name="playerHelper">Player helper for the player calling this command</param>
/// <param name="args">The arguments given with the command</param>
public static void Args(PlayerHelper playerHelper, string[] args) {
if (playerHelper == null) return;
if (!playerHelper.IsUser) {
playerHelper.ShowOverlay(Overlay.NotAuthorisedOnSyncPipes);
OverlayText.Hide(playerHelper.Player, 2f);
return;
}
switch (args.Length > 0 ? args[0] : null) {
case null : case "p" : playerHelper.TogglePlacingPipe(false);
break;
case "h" : case "help" : case "?" : Help(playerHelper);
break;
case "c" : case "copy" : Copy(playerHelper);
break;
case "r" : case "remove" : Remove(playerHelper);
break;
case "s" : case "stats" : Stats(playerHelper);
break;
case "n" : var name = string.Join(" ", args.Length > 1 ? args[1] : null);
Name(playerHelper, name);
break;
case "tc" : if (!playerHelper.IsAdmin) return;
if(playerHelper.State == PlayerHelper.UserState.ToolCupboard) playerHelper.StopToolCupboardBuildingId();
else playerHelper.StartToolCupboardBuildingId();
break;
}
}
/// <summary>
/// Start or stop placing a pipe
/// </summary>
/// <param name="playerHelper">The player calling the command</param>
public static void PlacePipe(PlayerHelper playerHelper) {
if (!playerHelper.IsUser) {
playerHelper.ShowOverlay(Overlay.NotAuthorisedOnSyncPipes);
OverlayText.Hide(playerHelper.Player, 2f);
return;
}
playerHelper?.TogglePlacingPipe(true);
}
/// <summary>
/// Displays help information in the player chat bar./// </summary>
/// <param name="playerHelper">Player requesting for help</param>
public static void Help(PlayerHelper playerHelper) {
if (playerHelper == null) return;
playerHelper.PrintToChatWithTitle(@"by Joe 90<size=5>
</size>
Based on <color=#80c5ff>j</color>Pipes by TheGreatJ");
playerHelper.PrintToChat(Chat.Commands);
playerHelper.PrintToChat(Chat.PipeMenuInstructions);
playerHelper.PrintToChat(Chat.UpgradePipes);
}
/// <summary>
/// Start or stop copying a pipe
/// </summary>
/// <param name="playerHelper">Player wanting to copy a pipe</param>
public static void Copy(PlayerHelper playerHelper) => playerHelper?.ToggleCopyingPipe();
/// <summary>
/// Switch into or out of remove pipe mode
/// </summary>
/// <param name="playerHelper">Player wanting to remove pipes</param>
public static void Remove(PlayerHelper playerHelper) {
if (!playerHelper.IsUser) {
playerHelper.ShowOverlay(Overlay.NotAuthorisedOnSyncPipes);
OverlayText.Hide(playerHelper.Player, 2f);
return;
}
playerHelper?.ToggleRemovingPipe();
}
/// <summary>
/// Show player stats about how many pipes they have, their pipe limit (if applicable) and the state of those pipes./// </summary>
/// <param name="playerHelper">Player requesting their stats</param>
public static void Stats(PlayerHelper playerHelper) {
if (playerHelper == null) return;
if (!playerHelper.IsUser) {
playerHelper.ShowOverlay(Overlay.NotAuthorisedOnSyncPipes);
OverlayText.Hide(playerHelper.Player, 2f);
return;
}
var total = playerHelper.Pipes.Count;
var running = 0;
foreach (var pipe in playerHelper.Pipes) {
if (pipe.Value.IsEnabled) running++;
}
var disabled = total - running;
playerHelper.PrintToChatWithTitle(playerHelper.PipeLimit != -1 ? Chat.StatsUnlimited : Chat.StatsLimited, total, playerHelper.PipeLimit, running, disabled );
}
/// <summary>
/// Open a pipe menu
/// </summary>
/// <param name="arg">Used to get the player and the pipe id</param>
public static void OpenMenu(ConsoleSystem.Arg arg) {
var playerHelper = PlayerHelper.Get(arg.Player());
if (!playerHelper.IsUser) {
playerHelper.ShowOverlay(Overlay.NotAuthorisedOnSyncPipes);
OverlayText.Hide(playerHelper.Player, 2f);
return;
}
GetPipe(arg)?.OpenMenu(playerHelper);
}
/// <summary>
/// Close a pipe menu
/// </summary>
/// <param name="arg">Used to get the player and the pipe id</param>
public static void CloseMenu(ConsoleSystem.Arg arg) => GetPipe(arg)?.CloseMenu(PlayerHelper.Get(arg.Player()));
/// <summary>
/// Start or stop naming a pipe or container
/// </summary>
/// <param name="playerHelper">Player wanting to name a pipe or container</param>
/// <param name="name">The name to be applied</param>
public static void Name(PlayerHelper playerHelper, string name) {
if (!playerHelper.IsUser) {
playerHelper.ShowOverlay(Overlay.NotAuthorisedOnSyncPipes);
OverlayText.Hide(playerHelper.Player, 2f);
return;
}
playerHelper.StartNaming(name);
}
/// <summary>
/// Close the player's menus.This is normally done when something affects the pipes they are looking at./// </summary>
/// <param name="playerHelper">The player to close the menus for</param>
public static void ForceCloseMenu(PlayerHelper playerHelper) => playerHelper?.CloseMenu();
/// <summary>
/// Adjust the priority of the pipe
/// </summary>
/// <param name="arg">Used to get the player, the pipe id (arg.Args[0]) and the amount to change the priority by (arg.Args[1])</param>
public static void ChangePriority(ConsoleSystem.Arg arg) {
var pipe = GetPipe(arg);
var change = GetInt(arg);
if(change.HasValue) pipe?.ChangePriority(change.Value);
}
/// <summary>
/// Refresh a menu for a player.Normally called when a pipe is changed./// </summary>
/// <param name="arg">Used to get the player and the pipe id</param>
public static void RefreshMenu(ConsoleSystem.Arg arg) => GetPipe(arg)?.OpenMenu(PlayerHelper.Get(arg.Player()));
/// <summary>
/// Helper to get the pipe from a given command arg
/// </summary>
/// <param name="arg">Used the get the pipe id from arg.Args</param>
/// <param name="index">Override the default index for the pipe id to be in the arg.Args</param>
/// <returns>The pipe with the given pipe id
/// If it is missing or not pipe is found then it will return null</returns>
private static Pipe GetPipe(ConsoleSystem.Arg arg, int index = 0) {
if (arg.Args.Length < index + 1) return null;
ulong pipeId;
return ulong.TryParse(arg.Args[index], out pipeId) ? Pipe.Get(pipeId) : null;
}
/// <summary>
/// Helper to get a boolean value from a given command arg
/// </summary>
/// <param name="arg">Used to get the value to be parsed as bool</param>
/// <param name="index">Set the index in the arg.Arg of the value to be parsed.</param>
/// <returns>The boolean value of the input./// If it's missing or invalid it will return false</returns>
private static bool GetBool(ConsoleSystem.Arg arg, int index = 1) {
if (arg.Args.Length < index + 1) return false;
return arg.Args[index]?.Equals(true.ToString()) ?? false;
}
/// <summary>
/// Helper to get a nullable integer value from a give command arg
/// </summary>
/// <param name="arg">Used to get the value to be parsed as int</param>
/// <param name="index">Set the index in the arg.Arg of the value to be parsed.</param>
/// <returns>The integer value of the input
/// If it's missing or invalid it will return null</returns>
private static int? GetInt(ConsoleSystem.Arg arg, int index = 1) {
if (arg.Args.Length < index + 1) return null;
int parseInt;
return int.TryParse(arg.Args[index], out parseInt) ? parseInt : default(int?);
}
/// <summary>
/// Turn the pipe on or off
/// </summary>
/// <param name="arg">Used to get the pipe id and requested pipe state</param>
public static void SetPipeState(ConsoleSystem.Arg arg) => GetPipe(arg)?.SetEnabled(GetBool(arg));
/// <summary>
/// Turn the pipe's auto start on or off
/// </summary>
/// <param name="arg">Used to get the pipe id and requested auto start state</param>
public static void SetPipeAutoStart(ConsoleSystem.Arg arg) => GetPipe(arg)?.SetAutoStart(GetBool(arg));
/// <summary>
/// Reverse the direction of the pipe
/// </summary>
/// <param name="arg">Used to get the pipe id</param>
public static void SwapPipeDirection(ConsoleSystem.Arg arg) => GetPipe(arg)?.SwapDirection();
/// <summary>
/// Set the pipe to single or multi stack
/// </summary>
/// <param name="arg">Used to get the pipe id and the requested stack mode
/// true : set to multi-stack mode
/// false: set to single-stack mode</param>
public static void SetPipeMultiStack(ConsoleSystem.Arg arg) => GetPipe(arg)?.SetMultiStack(GetBool(arg));
/// <summary>
/// Turns on or off the pipe's Furnace Splitter options
/// </summary>
/// <param name="arg">Used to get the pipe id and the request Furnace Splitter state</param>
public static void SetPipeFurnaceStackEnabled(ConsoleSystem.Arg arg) => GetPipe(arg)?.SetFurnaceStackEnabled(GetBool(arg));
/// <summary>
/// Sets the number of stacks in the pipe's Furnace Stack Splitter options
/// </summary>
/// <param name="arg">Used to get the pipe id and the requested Furnace Splitter stack count</param>
public static void SetPipeFurnaceStackCount(ConsoleSystem.Arg arg) {
var stackCount = GetInt(arg);
if (stackCount == null) return;
GetPipe(arg)?.SetFurnaceStackCount(stackCount.Value);
}
/// <summary>
/// Opens a loot container that allows players to control the items a pipe filters by
/// </summary>
/// <param name="arg">Used to get the pipe id and the player</param>
public static void OpenPipeFilter(ConsoleSystem.Arg arg) => GetPipe(arg)?.OpenFilter(PlayerHelper.Get(arg.Player()));
/// <summary>
/// Shows or hides help labels in pipe menu
/// </summary>
/// <param name="arg">Used to get the pipe id and the player</param>
public static void MenuHelp(ConsoleSystem.Arg arg) => PlayerHelper.Get(arg.Player())?.Menu.ToggleHelp();
// Flush the permissions of this player helper by forcing it to be recreated
public static void FlushPlayerPermissions(ConsoleSystem.Arg arg) => PlayerHelper.Remove(arg.Player());
}
#region Command Stubs
// These stubs are included as Oxide.Plugin needs all command and chat functions in the main class.void CommandArgs(IPlayer player, string command, string[] args) => Commands.Args(PlayerHelper.Get(player), args);
void CommandHelp(IPlayer player, string command, string[] args) => Commands.Help(PlayerHelper.Get(player));
void CommandCopy(IPlayer player, string command, string[] args) => Commands.Copy(PlayerHelper.Get(player));
void CommandRemove(IPlayer player, string command, string[] args) => Commands.Remove(PlayerHelper.Get(player));
void CommandStats(IPlayer player, string command, string[] args) => Commands.Stats(PlayerHelper.Get(player));
void CommandName(IPlayer player, string command, string[] args) => Commands.Name(PlayerHelper.Get(player), string.Join(" ", args));
[SyncPipesConsoleCommand("create")]
void StartPipe(ConsoleSystem.Arg arg) => Commands.PlacePipe(PlayerHelper.Get(arg.Player()));
[SyncPipesConsoleCommand("openmenu")]
void OpenMenu(ConsoleSystem.Arg arg) => Commands.OpenMenu(arg);
[SyncPipesConsoleCommand("closemenu")]
void CloseMenu(ConsoleSystem.Arg arg) => Commands.CloseMenu(arg);
[SyncPipesConsoleCommand("forceclosemenu")]
void ForceCloseMenu(ConsoleSystem.Arg arg) => Commands.ForceCloseMenu(PlayerHelper.Get(arg.Player()));
[SyncPipesConsoleCommand("refreshmenu")]
void RefreshMenu(ConsoleSystem.Arg arg) => Commands.RefreshMenu(arg);
[SyncPipesConsoleCommand("changepriority")]
void ChangePriority(ConsoleSystem.Arg arg) => Commands.ChangePriority(arg);
[SyncPipesConsoleCommand("setpipestate")]
void SetPipeState(ConsoleSystem.Arg arg) => Commands.SetPipeState(arg);
[SyncPipesConsoleCommand("setpipeautostart")]
void SetPipeAutoStart(ConsoleSystem.Arg arg) => Commands.SetPipeAutoStart(arg);
[SyncPipesConsoleCommand("swappipedirection")]
void SwapPipeDirection(ConsoleSystem.Arg arg) => Commands.SwapPipeDirection(arg);
[SyncPipesConsoleCommand("setpipemultistack")]
void SetPipeMultiStack(ConsoleSystem.Arg arg) => Commands.SetPipeMultiStack(arg);
[SyncPipesConsoleCommand("setpipefurnacestackenabled")]
void SetPipeFurnaceStackEnabled(ConsoleSystem.Arg arg) => Commands.SetPipeFurnaceStackEnabled(arg);
[SyncPipesConsoleCommand("setpipefurnacestackcount")]
void SetPipeFurnaceStackCount(ConsoleSystem.Arg arg) => Commands.SetPipeFurnaceStackCount(arg);
[SyncPipesConsoleCommand("openpipefilter")]
void OpenPipeFilter(ConsoleSystem.Arg arg) => Commands.OpenPipeFilter(arg);
[SyncPipesConsoleCommand("menuhelp")]
void MenuHelp(ConsoleSystem.Arg arg) => Commands.MenuHelp(arg);
[SyncPipesConsoleCommand("flushperms")]
void FlushPlayerPermissions(ConsoleSystem.Arg arg) => Commands.FlushPlayerPermissions(arg);
#endregion
/// <summary>
/// Helper to add the plugin commandPrefix to the start of each command./// </summary>
public class SyncPipesConsoleCommandAttribute : ConsoleCommandAttribute {
public SyncPipesConsoleCommandAttribute(string command) : base($"{nameof(SyncPipes).ToLower()}.{command}") {
}
}
#endregion
#region Config
class SyncPipesConfig {
private static readonly SyncPipesConfig Default = New();
public static SyncPipesConfig New() {
return new SyncPipesConfig {
FilterSizes = new List<int> {
0, 6, 18, 30, 42
}
, FlowRates = new List<int> {
1, 5, 10, 30, 50
}
, MaximumPipeDistance = 64f, MinimumPipeDistance = 2f, NoDecay = true, CommandPrefix = "p", HotKey = "p", UpdateRate = 2, AttachXmasLights = false, DestroyWithSalvage = false, PermissionLevels = new Dictionary<string, PermissionLevel> {
{
"sticks", new PermissionLevel {
MaximumGrade = 0, MaximumPipes = 15
}
}
, {
"wood", new PermissionLevel {
MaximumGrade = 1, MaximumPipes = 25
}
}
, {
"stone", new PermissionLevel {
MaximumGrade = 2, MaximumPipes = 35
}
}
, {
"metal", new PermissionLevel {
MaximumGrade = 3, MaximumPipes = 45
}
}
, {
"hqm", new PermissionLevel {
MaximumGrade = -1, MaximumPipes = -1
}
}
}
}
;
}
[JsonProperty("LogLevel")]
public int LogLevel {
get;
set;
}
= (int)LogLevels.Error;
[JsonProperty("filterSizes")]
public List<int> FilterSizes {
get;
set;
}
[JsonProperty("flowRates")]
public List<int> FlowRates {
get;
set;
}
[JsonProperty("maxPipeDist")]
public float MaximumPipeDistance {
get;
set;
}
[JsonProperty("minPipeDist")]
public float MinimumPipeDistance {
get;
set;
}
[JsonProperty("noDecay")]
public bool NoDecay {
get;
set;
}
[JsonProperty("commandPrefix")]
public string CommandPrefix {
get;
set;
}
[JsonProperty("hotKey")]
public string HotKey {
get;
set;
}
[JsonProperty("updateRate")]
public int UpdateRate {
get;
set;
}
[JsonProperty("xmasLights")]
public bool AttachXmasLights {
get;
set;
}
[JsonProperty("permLevels", DefaultValueHandling = DefaultValueHandling.Ignore)]
public Dictionary<string, PermissionLevel> PermissionLevels {
get;
set;
}
[JsonProperty("salvageDestroy")]
public bool DestroyWithSalvage {
get;
set;
}
= false;
[JsonProperty("experimental", DefaultValueHandling = DefaultValueHandling.Ignore)]
public ExperimentalConfig Experimental {
get;
set;
}
= new ExperimentalConfig();
[JsonProperty("blacklistTC")]
public bool BlacklistTC {
get;
set;
}
= false;
[JsonProperty("useQuickSmelt")]
public bool UseQuickSmelt {
get;
set;
}
= true;
public class PermissionLevel {
[JsonProperty("upgradeLimit")]
public int MaximumGrade {
get;
set;
}
= (int)BuildingGrade.Enum.TopTier;
[JsonProperty("pipeLimit")]
public int MaximumPipes {
get;
set;
}
= -1;
public static readonly PermissionLevel Default = new PermissionLevel() {
MaximumGrade = (int)BuildingGrade.Enum.Twigs, MaximumPipes = 0
}
;
}
private string[] Validate() {
var errors = new List<string>();
var filterSizeError = FilterSizes.Count != 5;
if (!filterSizeError) {
for (var i = 0; i < FilterSizes.Count; i++) {
if (FilterSizes[i] < 0 || FilterSizes[i] > 42) {
filterSizeError = true;
break;
}
}
}
if (filterSizeError) {
errors.Add("filterSizes must have 5 values between 0 and 42");
FilterSizes = new List<int>(Default.FilterSizes);
}
var flowRateError = FlowRates.Count != 5;
if (!flowRateError) {
for (var i = 0; i < FlowRates.Count; i++) {
if (FlowRates[i] <= 0) {
flowRateError = true;
break;
}
}
}
if (flowRateError) {
errors.Add("flowRates must have 5 values greater than 0");
FlowRates = new List<int>(Default.FlowRates);
}
if (UpdateRate <= 0) {
errors.Add("updateRage must be greater than 0");
UpdateRate = Default.UpdateRate;
}
return errors.ToArray();
}
public static SyncPipesConfig Load() {
try {
Instance.Puts("Loading Config");
var config = Instance.Config.ReadObject<SyncPipesConfig>();
if (config?.FilterSizes == null) {
Instance.Puts("Setting Defaults");
config = New();
Instance.Config.WriteObject(config);
}
var errors = config.Validate();
for (var i = 0; i < errors.Length; i++) Instance.PrintWarning(errors[i]);
if (errors.Length > 0) {
Instance.PrintError("Invalid config file.Using default configs.");
return Default;
}
return config;
}
catch (Exception e) {
Logger.Runtime.LogException(e, "Config.Load");
Instance.PrintError("Invalid config file.Using default configs.");
return Default;
}
}
/// <summary>
/// Register the level permission keys to Oxide
/// </summary>
public void RegisterPermissions() {
if (PermissionLevels != null) {
foreach (var permissionKey in PermissionLevels.Keys) {
Instance.permission.RegisterPermission($"{Instance.Name}.level.{permissionKey}", Instance);
}
}
}
}
class ExperimentalConfig {
[JsonProperty("barrelPipe")]
public bool BarrelPipe {
get;
set;
}
[JsonProperty("permEntity")]
public bool PermanentEntities {
get;
set;
}
}
/// <summary>
/// Oxide hook for loading default config settings
/// </summary>
protected override void LoadDefaultConfig() {
Config?.Clear();
_config = SyncPipesConfig.New();
Config?.WriteObject(_config);
SaveConfig();
}
/// <summary>
/// Config for this plugin instance./// </summary>
static SyncPipesConfig InstanceConfig => Instance._config;
private SyncPipesConfig _config;
// the config store for this plugin instance
/// <summary>
/// New Hook: Exposes the No Decay config to external plugins
/// </summary>
private bool IsNoDecayEnabled => InstanceConfig.NoDecay;
#endregion
#region ContainerHelper
public const string FUEL_STORAGE_PREFAB = "fuelstorage";
public const string QUARRY_OUTPUT_PREFAB = "hopperoutput";
public const string PUMPJACK_OUTPUT_PREFAB = "crudeoutput";
/// <summary>
/// This helps find containers and the required information needed to attach pipes
/// </summary>
static class ContainerHelper {
/// <summary>
/// Lists all the container types that pipes cannot connect to
/// </summary>
/// <param name="container">The container to check</param>
/// <returns>True if the container type is blacklisted</returns>
public static bool IsBlacklisted(BaseEntity container) {
return (InstanceConfig.BlacklistTC && container.ShortPrefabName == ToolCupboardPrefab) || container is BaseFuelLightSource || container is Locker || container is ShopFront || container is RepairBench || container is LootContainer;
}
/// <summary>
/// Get a storage container from its Id
/// </summary>
/// <param name="id">The Id to search for</param>
/// <returns>The container that matches the id</returns>
public static StorageContainer Find(uint id) {
return Find((BaseEntity)BaseNetworkable.serverEntities.Find(id));
}
/// <summary>
/// Get the container id and the startable type from a container
/// </summary>
/// <param name="container">The container to get the data for</param>
public static ContainerType GetEntityType(BaseEntity container) {
if (container is BaseOven) return ContainerType.Oven;
if (container is Recycler) return ContainerType.Recycler;
if (container is ResourceExtractorFuelStorage) {
switch (container.ShortPrefabName) {
case FUEL_STORAGE_PREFAB : return ContainerType.FuelStorage;
case QUARRY_OUTPUT_PREFAB : return ContainerType.QuarryOutput;
case PUMPJACK_OUTPUT_PREFAB : return ContainerType.PumpJackOutput;
}
}
return ContainerType.General;
}
public static bool InMonument(BaseEntity entity) {
switch (GetEntityType(entity)) {
case ContainerType.PumpJackOutput : case ContainerType.QuarryOutput : case ContainerType.FuelStorage : case ContainerType.Recycler : for (int i = 0; i < TerrainMeta.Path.Monuments.Count; i++) {
var monument = TerrainMeta.Path.Monuments[i];
if (monument.IsInBounds(entity.transform.position)) return false;
}
break;
}
return true;
}
private static void LogFindError(uint parentId, BaseEntity entity, ContainerType containerType, List<BaseEntity> children = null) {
Logger.FindErrors.Log("------------------- {0} -------------------", parentId);
if (entity == null) Logger.FindErrors.Log("Entity not found");
else Logger.FindErrors.Log("Entity: {0} ({1})", entity.ShortPrefabName, entity);
Logger.FindErrors.Log("Type: {0}", containerType);
for (int i = 0; i < children?.Count; i++) Logger.FindErrors.Log("Child {0}: {1} ({2})", i, children[i].ShortPrefabName, children[i]);
Logger.FindErrors.Log("");
}
public static BaseEntity Find(uint parentId, ContainerType containerType) {
var entity = (BaseEntity) BaseNetworkable.serverEntities.Find(parentId);
return Find(entity, containerType);
}
public static BaseEntity Find(BaseEntity entity, ContainerType containerType) {
if (entity == null) {
LogFindError(entity.net.ID, null, containerType);
return null;
}
if (!IsComplexStorage(containerType)) return entity;
BaseResourceExtractor resourceExtractor = null;
if (entity?.TryGetComponent<BaseResourceExtractor>(out resourceExtractor) ?? false) {
var children = resourceExtractor.children;
var prefabName = GetShortPrefabName(containerType);
for (var i = 0; i < children?.Count; i++) {
if (children[i].ShortPrefabName == prefabName) return children[i] as ResourceExtractorFuelStorage;
}
LogFindError(entity.net.ID, entity, containerType, children);
}
else {
LogFindError(entity.net.ID, entity, containerType);
}
return null;
}
public static StorageContainer Find(BaseEntity parent) {
StorageContainer container = null;
return parent?.TryGetComponent<StorageContainer>(out container) ?? false ? container : null;
}
public static string GetShortPrefabName(ContainerType containerType) {
switch (containerType) {
case ContainerType.FuelStorage : return FUEL_STORAGE_PREFAB;
case ContainerType.QuarryOutput : return QUARRY_OUTPUT_PREFAB;
case ContainerType.PumpJackOutput : return PUMPJACK_OUTPUT_PREFAB;
}
return "";
}
public static bool IsComplexStorage(ContainerType containerType) {
switch (containerType) {
case ContainerType.FuelStorage : case ContainerType.PumpJackOutput : case ContainerType.QuarryOutput : return true;
default : return false;
}
}
public static bool CanAutoStart(ContainerType containerType) {
switch (containerType) {
case ContainerType.FuelStorage : case ContainerType.Oven : case ContainerType.Recycler : return true;
default : return false;
}
}
}
/// <summary>
/// Entity Types
/// </summary>
public enum ContainerType {
General, Oven, Recycler, FuelStorage, QuarryOutput, PumpJackOutput, ResourceExtractor
}
#endregion
#region ContainerManager
/// <summary>
/// This is attached to a Storage Container to act as the controller for moving items through pipes./// This then allows for items to move through pipes in a more synchronous manner./// Items can be split evenly between all pipes of the same priority./// </summary>
public class ContainerManager : MonoBehaviour {
///// <summary>
///// Get the save data for all container managers
///// </summary>
///// <returns>data for all container managers</returns>
//public static IEnumerable<DataStore> Save()
//{
// using (var enumerator = ManagedContainerLookup.GetEnumerator())
// {
// while (enumerator.MoveNext())
// {
// if (enumerator.Current.Value.
// ... [Code preview truncated due to length. Full source in attached file] ...