C#:
using System;
using System.Collections.Generic;
using System.Collections;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine.Networking;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("MirrorPanel", "tama", "1.0.5")]
public class MirrorPanel : RustPlugin
{
#region [Vars]
[PluginReference] private Plugin MirrorAlerts = null;
private const String Layer = "MirrorPanel.Layer";
private static InterfaceBuilder _interface = null;
private static ImageUI ImageUi = null;
private static MirrorPanel _ = null;
private HashSet<UInt64> updatePanel = new HashSet<UInt64>();
private HashSet<UInt64> openButton = new HashSet<UInt64>();
public List<UInt64> lootedContainerPlayers = new List<UInt64>();
private Dictionary<UInt64, DateTime> _cooldownPlayer = new Dictionary<UInt64, DateTime>();
private Dictionary<String, String> Button = new Dictionary<String, String>()
{
["Kits"] = "/kit",
["Report"] = "/report",
["chat"] = "/chat",
["store"] = "/store" // Добавлена кнопка
};
private Int32 Online = 0;
private Timer updateTimer;
#endregion
#region [Oxide-Api]
private void Init() => _ = this;
private void OnServerInitialized()
{
ImageUi = new ImageUI();
ImageUi.DownloadImage();
// НАКРУТКА: Добавляем 22 к текущему онлайну при инициализации
Online = BasePlayer.activePlayerList.Count + 22;
updateTimer = timer.Every(5f, () => UpdatePanel());
}
private void Unload()
{
if (updateTimer != null)
{
updateTimer.Destroy();
updateTimer = null;
}
if (ImageUi != null)
{
ImageUi.UnloadImages();
ImageUi = null;
}
InterfaceBuilder.DestroyAll();
if (updatePanel != null)
{
updatePanel.Clear();
updatePanel = null;
}
if (openButton != null)
{
openButton.Clear();
openButton = null;
}
if (Button != null)
{
Button.Clear();
Button = null;
}
_ = null;
}
#endregion
#region [Rust-Api]
private void OnPlayerConnected(BasePlayer player)
{
// ИЗМЕНЕНИЕ: Убрано автоматическое добавление в updatePanel.
// Теперь при входе панель считается закрытой (только Лого).
if (updatePanel.Contains(player.userID))
updatePanel.Remove(player.userID);
DrawUI(player);
}
private void OnEntityEnter(TriggerWorkbench trigger, BasePlayer player)
{
if (player == null || player.IsNpc || lootedContainerPlayers.Contains(player.userID)) return;
CuiHelper.DestroyUi(player, Layer);
lootedContainerPlayers.Add(player.userID);
}
private void OnEntityLeave(TriggerWorkbench trigger, BasePlayer player)
{
if (player == null || player.IsNpc || !lootedContainerPlayers.Contains(player.userID)) return;
DrawUI(player);
lootedContainerPlayers.Remove(player.userID);
}
#endregion
#region [API]
private Boolean IsUpdatePanel(UInt64 steamId) => updatePanel.Contains(steamId) && !lootedContainerPlayers.Contains(steamId);
#endregion
#region [Command]
[ConsoleCommand("UI_MirrorPanel")]
private void MirrorPanelUIHandler(ConsoleSystem.Arg args)
{
BasePlayer player = args?.Player();
if (player == null || !args.HasArgs() || _cooldownPlayer.ContainsKey(player.userID) && _cooldownPlayer[player.userID].Subtract(DateTime.Now).TotalSeconds >= 0) return;
_cooldownPlayer[player.userID] = DateTime.Now.AddSeconds(0.2f);
switch (args.Args[0])
{
case "logo":
{
if (updatePanel.Contains(player.userID))
updatePanel.Remove(player.userID);
else
updatePanel.Add(player.userID);
DrawUI(player);
break;
}
case "button":
{
if (openButton.Contains(player.userID))
{
openButton.Remove(player.userID);
DestroyButtons(player);
}
else
{
openButton.Add(player.userID);
DrawButtonUI(player);
}
break;
}
}
}
#endregion
#region [InterfaceBuilder]
private class InterfaceBuilder
{
public static InterfaceBuilder Instance;
public const String Layer = "MirrorPanel.Layer";
public Dictionary<String, String> Interfaces;
public InterfaceBuilder()
{
Instance = this;
Interfaces = new Dictionary<String, String>();
BuildingPlayer_OpenMenu();
BuildingPlayer_CloseMenu();
Building_Button_DropMenu();
}
public static void AddInterface(String name, String json)
{
if (Instance.Interfaces.ContainsKey(name))
{
_.PrintError($"Error! Tried to add existing cui elements! -> {name}");
return;
}
Instance.Interfaces.Add(name, json);
}
public static String GetInterface(String name)
{
String json = String.Empty;
if (Instance != null && Instance.Interfaces.TryGetValue(name, out json) == false)
{
_.PrintWarning($"Warning! UI elements not found by name! -> {name}");
}
return json;
}
public static void DestroyAll()
{
foreach (BasePlayer player in BasePlayer.activePlayerList)
CuiHelper.DestroyUi(player, Layer);
}
private void BuildingPlayer_OpenMenu()
{
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel
{
RectTransform = { AnchorMin = "0 1", AnchorMax = "0 1", OffsetMin = "0 0", OffsetMax = "0 0" },
Image = { Color = "0 0 0 0" }
}, "Overlay", Layer, Layer);
container.Add(new CuiElement
{
Name = $"{Layer}.L",
Parent = Layer,
Components =
{
new CuiRawImageComponent { Png = ImageUi.GetImage("Logo"), Sprite = "assets/icons/loading.png" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "5 -31", OffsetMax = "31 -5" }
}
});
container.Add(new CuiButton
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" },
Button = { Color = "0 0 0 0", Command = "UI_MirrorPanel logo" },
Text = { Text = "" }
}, $"{Layer}.L");
container.Add(new CuiLabel()
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "33 -36", OffsetMax = "208 0" },
Text = { Text = $"<color=#FF0000>SABR</color> RUST №1", Color = "1 1 1 1", Align = TextAnchor.MiddleLeft, FontSize = 22, Font = "robotocondensed-bold.ttf" }
}, Layer);
container.Add(new CuiButton
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "5 -54", OffsetMax = "51 -33" },
Button = { Color = "0 0 0 0", Command = "UI_MirrorPanel button" },
Text = { Text = "%MENU%", Color = "0.92 0.92 0.92 1.00", Align = TextAnchor.MiddleCenter, FontSize = 16, Font = "robotocondensed-bold.ttf" }
}, Layer);
container.Add(new CuiElement
{
Name = $"{Layer}.Online",
Parent = Layer,
Components =
{
new CuiRawImageComponent { Png = ImageUi.GetImage("Online"), Sprite = "assets/icons/loading.png", Color = "1 1 1 0.8" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "54 -54", OffsetMax = "99 -33" }
}
});
container.Add(new CuiElement
{
Name = $"{Layer}.Online.Text",
Parent = $"{Layer}.Online",
Components =
{
new CuiTextComponent { Text = $"%ONLINE%/{ConVar.Server.maxplayers}", Color = "1 1 1 0.95", Align = TextAnchor.MiddleCenter, FontSize = 12, Font = "robotocondensed-bold.ttf" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
},
});
container.Add(new CuiElement
{
Name = $"{Layer}.Time",
Parent = Layer,
Components =
{
new CuiRawImageComponent { Png = ImageUi.GetImage("Time"), Sprite = "assets/icons/loading.png", Color = "1 1 1 0.8" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "102 -54", OffsetMax = "140 -33" }
}
});
container.Add(new CuiElement
{
Name = $"{Layer}.Time.Text",
Parent = $"{Layer}.Time",
Components =
{
new CuiTextComponent { Text = "%TIME%", Color = "1 1 1 0.95", Align = TextAnchor.MiddleCenter, FontSize = 12, Font = "robotocondensed-bold.ttf" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
},
});
AddInterface("UI_OPEN_MENU", container.ToJson());
}
private void BuildingPlayer_CloseMenu()
{
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel
{
RectTransform = { AnchorMin = "0 1", AnchorMax = "0 1", OffsetMin = "0 0", OffsetMax = "0 0" },
Image = { Color = "0 0 0 0" }
}, "Overlay", Layer, Layer);
container.Add(new CuiElement
{
Name = $"{Layer}.L",
Parent = Layer,
Components =
{
new CuiRawImageComponent { Png = ImageUi.GetImage("Logo"), Sprite = "assets/icons/loading.png" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "5 -31", OffsetMax = "31 -5" }
}
});
container.Add(new CuiButton
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" },
Button = { Color = "0 0 0 0", Command = "UI_MirrorPanel logo" },
Text = { Text = "" }
}, $"{Layer}.L");
AddInterface("UI_CLOSE_MENU", container.ToJson());
}
private void Building_Button_DropMenu()
{
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = $"5 %OFFSET_MIN%", OffsetMax = $"130 %OFFSET_MAX%" },
Image = { Color = "0 0 0 0" }
}, Layer, "%NAME_BUTTON%");
container.Add(new CuiElement
{
Name = "Image",
Parent = "%NAME_BUTTON%",
Components =
{
new CuiImageComponent { Png = "%PNG_BUTTON%", Color = "0.92 0.92 0.92 1.00" },
new CuiRectTransformComponent { AnchorMin = "0 0.5", AnchorMax = "0 0.5", OffsetMin = "0 -10", OffsetMax = "20 10" }
}
});
container.Add(new CuiLabel()
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "25 0", OffsetMax = "0 0" },
Text = { Text = "%TEXT%", Color = "0.92 0.92 0.92 1.00", Align = TextAnchor.MiddleLeft, FontSize = 12, Font = "robotocondensed-bold.ttf" }
}, "%NAME_BUTTON%");
container.Add(new CuiButton
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" },
Button = { Color = "0 0 0 0", Command = $"chat.say %COMMAND%" },
Text = { Text = "" }
}, "%NAME_BUTTON%");
AddInterface("MENU_DROP_BUTTON", container.ToJson());
}
}
#endregion
#region [ImageUI]
private class ImageUI
{
private const String _path = "MirrorPanel/";
private const String _printPath = "data/" + _path;
private readonly Dictionary<String, ImageData> _images = new Dictionary<String, ImageData>()
{
{ "Logo", new ImageData() },
{ "Group 1", new ImageData() },
{ "Online", new ImageData() },
{ "Time", new ImageData() },
{ "Kits", new ImageData() },
{ "Report", new ImageData() },
{ "chat", new ImageData() },
{ "store", new ImageData() } // Добавлена картинка store.png
};
private enum ImageStatus
{
NotLoaded,
Loaded,
Failed
}
private class ImageData
{
public ImageStatus Status = ImageStatus.NotLoaded;
public String Id { get; set; }
}
public String GetImage(String name)
{
ImageData image;
if (_images.TryGetValue(name, out image) && image.Status == ImageStatus.Loaded)
return image.Id;
return null;
}
public void DownloadImage()
{
KeyValuePair<String, ImageData>? image = null;
foreach (KeyValuePair<String, ImageData> img in _images)
{
if (img.Value.Status == ImageStatus.NotLoaded)
{
image = img;
break;
}
}
if (image != null)
{
ServerMgr.Instance.StartCoroutine(ProcessDownloadImage(image.Value));
}
else
{
List<String> failedImages = new List<String>();
foreach (KeyValuePair<String, ImageData> img in _images)
{
if (img.Value.Status == ImageStatus.Failed)
{
failedImages.Add(img.Key);
}
}
if (failedImages.Count > 0)
{
String images = String.Join(", ", failedImages);
_.PrintError($"Не удалось загрузить следующие изображения: {images}. Возможно, вы не загрузили их в папку '{_printPath}'.");
Interface.Oxide.UnloadPlugin(_.Name);
}
else
{
_.Puts($"{_images.Count} изображений успешно загружено!");
_interface = new InterfaceBuilder();
_.PrintWarning("Интерфейс успешно загружен!");
foreach (BasePlayer player in BasePlayer.activePlayerList)
_.OnPlayerConnected(player);
}
}
}
public void UnloadImages()
{
foreach (KeyValuePair<String, ImageData> item in _images)
if (item.Value.Status == ImageStatus.Loaded)
if (item.Value?.Id != null)
FileStorage.server.Remove(uint.Parse(item.Value.Id), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
_images?.Clear();
}
private IEnumerator ProcessDownloadImage(KeyValuePair<String, ImageData> image)
{
String url = $"file://{Interface.Oxide.DataDirectory}/{_path}{image.Key}.png";
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
image.Value.Status = ImageStatus.Failed;
}
else
{
Texture2D tex = DownloadHandlerTexture.GetContent(www);
image.Value.Id = FileStorage.server.Store(tex.EncodeToPNG(), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
image.Value.Status = ImageStatus.Loaded;
UnityEngine.Object.DestroyImmediate(tex);
}
DownloadImage();
}
}
}
#endregion
#region [Lang]
private new void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<String, String>
{
["Kits"] = "KITS",
["Report"] = "REPORTS",
["Menu"] = "MENU",
["chat"] = "CHAT SETTINGS",
["store"] = "STORE" // Добавлен английский ключ
}, this);
lang.RegisterMessages(new Dictionary<String, String>
{
["Kits"] = "НАБОРЫ",
["Report"] = "ЖАЛОБЫ",
["Menu"] = "МЕНЮ",
["chat"] = "НАСТРОЙКИ ЧАТА",
["store"] = "КОРЗИНА" // Добавлен русский ключ
}, this, "ru");
}
#endregion
#region [Functional]
private void DrawUI(BasePlayer player)
{
if (_interface == null) return;
if (updatePanel.Contains(player.userID))
{
String Interface = InterfaceBuilder.GetInterface("UI_OPEN_MENU");
if (Interface == null) return;
Interface = Interface.Replace("%MENU%", lang.GetMessage("Menu", this, player.UserIDString));
Interface = Interface.Replace("%ONLINE%", $"{Online}");
Interface = Interface.Replace("%TIME%", $"{TOD_Sky.Instance.Cycle.DateTime:HH:mm}");
CuiHelper.AddUi(player, Interface);
if (openButton.Contains(player.userID)) DrawButtonUI(player);
}
else
{
String Interface = InterfaceBuilder.GetInterface("UI_CLOSE_MENU");
if (Interface == null) return;
CuiHelper.AddUi(player, Interface);
}
}
private void DrawButtonUI(BasePlayer player)
{
Int32 Y = 0;
foreach (KeyValuePair<String, String> button in Button)
{
DrawUI_ButtonDropMenu(player, Y, button.Value, button.Key, lang.GetMessage(button.Key, this, player.UserIDString));
Y++;
}
}
private void DrawUI_ButtonDropMenu(BasePlayer player, Int32 Y, String Command, String PNG, String TitleBtn)
{
String Interface = InterfaceBuilder.GetInterface("MENU_DROP_BUTTON");
if (Interface == null) return;
Interface = Interface.Replace("%NAME_BUTTON%", $"DROP_BUTTON_{Y}");
Interface = Interface.Replace("%OFFSET_MIN%", $"{-77 - (Y * 23)}");
Interface = Interface.Replace("%OFFSET_MAX%", $"{-57 - (Y * 23)}");
Interface = Interface.Replace("%PNG_BUTTON%", ImageUi.GetImage(PNG));
Interface = Interface.Replace("%TEXT%", TitleBtn);
Interface = Interface.Replace("%COMMAND%", Command);
CuiHelper.AddUi(player, Interface);
}
private void DestroyButtons(BasePlayer player)
{
for (Int32 Y = 0; Y < Button.Count; Y++)
CuiHelper.DestroyUi(player, $"DROP_BUTTON_{Y}");
}
private void UpdatePanel()
{
if (updatePanel.Count == 0) return;
try
{
int currentFakeOnline = BasePlayer.activePlayerList.Count + 0;
bool onlineChanged = Online != currentFakeOnline;
if (onlineChanged)
Online = currentFakeOnline;
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
if (!updatePanel.Contains(player.userID) || lootedContainerPlayers.Contains(player.userID))
continue;
if (onlineChanged)
RefreshOnlineText(player);
RefreshTimeText(player);
}
}
catch (Exception e)
{
PrintError($"Ошибка при обновлении панели: {e}");
}
}
private void RefreshOnlineText(BasePlayer player)
{
CuiHelper.DestroyUi(player, $"{Layer}.Online.Text");
var container = new CuiElementContainer();
container.Add(new CuiElement
{
Name = $"{Layer}.Online.Text",
Parent = $"{Layer}.Online",
Components =
{
new CuiTextComponent { Text = $"{Online}/{ConVar.Server.maxplayers}", Color = "1 1 1 0.95", Align = TextAnchor.MiddleCenter, FontSize = 12, Font = "robotocondensed-bold.ttf" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
}
});
CuiHelper.AddUi(player, container);
}
private void RefreshTimeText(BasePlayer player)
{
CuiHelper.DestroyUi(player, $"{Layer}.Time.Text");
var container = new CuiElementContainer();
container.Add(new CuiElement
{
Name = $"{Layer}.Time.Text",
Parent = $"{Layer}.Time",
Components =
{
new CuiTextComponent { Text = TOD_Sky.Instance.Cycle.DateTime.ToString("HH:mm"), Color = "1 1 1 0.95", Align = TextAnchor.MiddleCenter, FontSize = 12, Font = "robotocondensed-bold.ttf" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
}
});
CuiHelper.AddUi(player, container);
}
#endregion
}
}