Иконка ресурса

FREE RustDiffusion : 1.0.0 1.0.0

Нет прав для скачивания
C#:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;
using Graphics = System.Drawing.Graphics;
using Color = System.Drawing.Color;

//  ____  _       _ _       
// / ___|(_) __ _(_) | ___  
// \___ \| |/ _` | | |/ _ \ 
//  ___) | | (_| | | | (_) |
// |____/|_|\__, |_|_|\___/ 
//          |___/             
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
// ✦    RUST PLUGINS   ✦
// ✦     Sigilo.dev    ✦
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾

namespace Oxide.Plugins
{
    [Info("Rust Diffusion", "Sigilo", "1.0.0")]
    [Description("Generates images on signs using difussion models")]
    public class RustDiffusion : RustPlugin
    {
        #region Configuration

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("API Provider (Google, StableDiffusion, OpenAI)")]
            public string Provider { get; set; } = "OpenAI";

            [JsonProperty("API Key (Google/OpenAI)")]
            public string ApiKey { get; set; } = "YOUR_API_KEY_HERE";

            [JsonProperty("API Base URL")]
            public string ApiBaseUrl { get; set; } = "https://api.openai.com/v1/images/generations";

            [JsonProperty("Model ID")]
            public string ModelId { get; set; } = "gpt-image-1-mini";

            [JsonProperty("Command Cooldown (seconds)")]
            public float Cooldown { get; set; } = 600f;

            [JsonProperty("Daily Request Limit")]
            public int DailyLimit { get; set; } = 10;

            [JsonProperty("Admins Unlimited")]
            public bool AdminsUnlimited { get; set; } = true;

            [JsonProperty("Max Interaction Distance")]
            public float MaxDistance { get; set; } = 3f;

            [JsonProperty("Blocked Words")]
            public List<string> BlockedWords { get; set; }

            [JsonProperty("Debug Mode")]
            public bool Debug { get; set; } = false;

            [JsonProperty("Discord Webhook URL")]
            public string DiscordWebhookUrl { get; set; } = "";

            [JsonProperty("Loading Image URL")]
            public string LoadingImageUrl { get; set; } = "https://sigilo.dev/assets/images/site/loading.png";
        }

        private class StoredData
        {
            public Dictionary<ulong, PlayerData> Players = new Dictionary<ulong, PlayerData>();
        }

        private class PlayerData
        {
            public int DailyCount;
            public string LastDayUsed;
        }

        private StoredData storedData;

        private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("RustDiffusion", storedData);

        protected override void LoadDefaultConfig()
        {
            config = new PluginConfig
            {
                BlockedWords = new List<string> { "nsfw", "nude", "porn" }
            };
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            config = Config.ReadObject<PluginConfig>();
            if (config.BlockedWords != null)
                config.BlockedWords = config.BlockedWords.Distinct().ToList();
            SaveConfig();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(config);
        }

        #endregion

        #region Data Structures

        private Dictionary<string, DiffusionSize> DiffusionSizePerAsset;
        private Dictionary<ulong, float> cooldowns = new Dictionary<ulong, float>();
        private GameObject behaviorGameObject;
        private DiffusionBehavior behavior;

        private class DiffusionSize
        {
            public int Width { get; }
            public int Height { get; }
            public int ImageWidth { get; }
            public int ImageHeight { get; }

            public DiffusionSize(int width, int height) : this(width, height, width, height) { }
            public DiffusionSize(int width, int height, int imageWidth, int imageHeight)
            {
                Width = width;
                Height = height;
                ImageWidth = imageWidth;
                ImageHeight = imageHeight;
            }
        }

        #endregion

        #region Initialization

        private void Init()
        {
            try {
                storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("RustDiffusion");
            } catch { }
            
            if (storedData == null)
            {
                storedData = new StoredData();
                SaveData();
            }

            InitializeDiffusionSizes();
            permission.RegisterPermission("rustdiffusion.use", this);
            permission.RegisterPermission("rustdiffusion.noblock", this);
            AddCovalenceCommand("draw", "CmdDraw");
            AddCovalenceCommand("listmodels", "CmdListModels");
        }

        private void OnServerInitialized()
        {
            behaviorGameObject = new GameObject("RustDiffusionBehavior");
            behavior = behaviorGameObject.AddComponent<DiffusionBehavior>();
            behavior.plugin = this;
        }

        private void Unload()
        {
            if (behaviorGameObject != null)
                UnityEngine.Object.Destroy(behaviorGameObject);
        }

        private void InitializeDiffusionSizes()
        {

            DiffusionSizePerAsset = new Dictionary<string, DiffusionSize>
            {
                ["sign.pictureframe.landscape"] = new DiffusionSize(256, 192),
                ["sign.pictureframe.portrait"] = new DiffusionSize(205, 256),
                ["sign.pictureframe.tall"] = new DiffusionSize(128, 512),
                ["sign.pictureframe.xl"] = new DiffusionSize(512, 512),
                ["sign.pictureframe.xxl"] = new DiffusionSize(1024, 512),
                ["sign.small.wood"] = new DiffusionSize(256, 128),
                ["sign.medium.wood"] = new DiffusionSize(512, 256),
                ["sign.large.wood"] = new DiffusionSize(512, 256),
                ["sign.huge.wood"] = new DiffusionSize(1024, 256),
                ["sign.hanging.banner.large"] = new DiffusionSize(256, 1024),
                ["sign.pole.banner.large"] = new DiffusionSize(256, 1024),
                ["sign.hanging"] = new DiffusionSize(256, 512),
                ["sign.hanging.ornate"] = new DiffusionSize(512, 256),
                ["sign.post.single"] = new DiffusionSize(256, 128),
                ["sign.post.double"] = new DiffusionSize(512, 512),
                ["sign.post.town"] = new DiffusionSize(512, 256),
                ["sign.post.town.roof"] = new DiffusionSize(512, 256),
                ["photoframe.large"] = new DiffusionSize(320, 240),
                ["photoframe.portrait"] = new DiffusionSize(320, 384),
                ["photoframe.landscape"] = new DiffusionSize(320, 240),
                ["sign.neon.xl.animated"] = new DiffusionSize(256, 256),
                ["sign.neon.xl"] = new DiffusionSize(256, 256),
                ["sign.neon.125x215.animated"] = new DiffusionSize(128, 256),
                ["sign.neon.125x215"] = new DiffusionSize(128, 256),
                ["sign.neon.125x125"] = new DiffusionSize(128, 128),
                ["spinner.wheel.deployed"] = new DiffusionSize(512, 512, 285, 285),
                ["carvable.pumpkin"] = new DiffusionSize(256, 256),
            };
        }

        #endregion

        #region Commands

        [Command("draw")]
        private void CmdDraw(IPlayer iPlayer, string command, string[] args)
        {
            var player = iPlayer.Object as BasePlayer;
            if (player == null) return;

            if (!permission.UserHasPermission(player.UserIDString, "rustdiffusion.use"))
            {
                player.ChatMessage("You do not have permission to use this command.");
                return;
            }

            if (args.Length == 0)
            {
                player.ChatMessage("Usage: /draw <prompt>");
                return;
            }


            if (!((config.AdminsUnlimited && player.IsAdmin) || permission.UserHasPermission(player.UserIDString, "rustdiffusion.noblock")) && config.Cooldown > 0)
            {
                if (cooldowns.TryGetValue(player.userID, out float lastUse))
                {
                    float remaining = config.Cooldown - (UnityEngine.Time.realtimeSinceStartup - lastUse);
                    if (remaining > 0)
                    {
                        player.ChatMessage($"Please wait {Math.Ceiling(remaining)}s before generating another image.");
                        return;
                    }
                }
            }


            if (player.IsBuildingBlocked())
            {
                player.ChatMessage("You cannot use this command while building blocked.");
                return;
            }


            if (!GetTargetSign(player, out var paintable, out var signEntity, out string distanceError))
            {
                player.ChatMessage(distanceError ?? "You are not looking at a valid sign.");
                return;
            }

            if (!paintable.CanUpdate(player) && !permission.UserHasPermission(player.UserIDString, "rustdiffusion.noblock"))
            {
                player.ChatMessage("You are not authorized to edit this sign.");
                return;
            }


            string prompt = string.Join(" ", args);
            string sanitizedPrompt = SanitizePrompt(prompt);

            if (string.IsNullOrWhiteSpace(sanitizedPrompt))
            {
                player.ChatMessage("Invalid prompt.");
                return;
            }

            if (!((config.AdminsUnlimited && player.IsAdmin) || permission.UserHasPermission(player.UserIDString, "rustdiffusion.noblock")))
            {
                foreach (var badWord in config.BlockedWords)
                {
                    if (sanitizedPrompt.ToLower().Contains(badWord.ToLower()))
                    {
                        player.ChatMessage("Your prompt contains forbidden words.");
                        return;
                    }
                }

    
                if (!storedData.Players.TryGetValue(player.userID, out var pData))
                {
                    pData = new PlayerData { DailyCount = 0, LastDayUsed = DateTime.Now.ToString("yyyy-MM-dd") };
                    storedData.Players[player.userID] = pData;
                }

                string today = DateTime.Now.ToString("yyyy-MM-dd");
                if (pData.LastDayUsed != today)
                {
                    pData.DailyCount = 0;
                    pData.LastDayUsed = today;
                    SaveData();
                }

                if (pData.DailyCount >= config.DailyLimit)
                {
                     player.ChatMessage($"You have reached your daily limit of {config.DailyLimit} images. Please try again tomorrow.");
                     return;
                }
            }
            

            player.ChatMessage($"Generating image for '{sanitizedPrompt}'... please wait.");
            
            bool isHanging = signEntity.ShortPrefabName == "sign.hanging";
            uint textureIndex = 0; 
            
            cooldowns[player.userID] = UnityEngine.Time.realtimeSinceStartup;

            if (!((config.AdminsUnlimited && player.IsAdmin) || permission.UserHasPermission(player.UserIDString, "rustdiffusion.noblock")))
            {
                if (storedData.Players.TryGetValue(player.userID, out var data))
                {
                   data.DailyCount++;
                   SaveData();
                }
            }

            behavior.QueueGeneration(player, paintable, signEntity, sanitizedPrompt, textureIndex, isHanging);
        }

        [Command("listmodels")]
        private void CmdListModels(IPlayer iPlayer, string command, string[] args)
        {
            var player = iPlayer.Object as BasePlayer;
            

            if (player != null && !player.IsAdmin) 
            {
                player.ChatMessage("You do not have permission to list models.");
                return;
            }

            behavior.ListAvailableModels(player);
        }

        #endregion

        #region Logic
        
        private string SanitizePrompt(string input)
        {

            return Regex.Replace(input, @"[^a-zA-Z0-9 ,.\-!?'""]", "");
        }

        private bool GetTargetSign(BasePlayer player, out IDiffusionPaintable paintable, out BaseEntity entity, out string error)
        {
            paintable = null;
            entity = null;
            error = null;
            
            RaycastHit hit;

            if (Physics.Raycast(player.eyes.HeadRay(), out hit, 20f, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Ignore))
            {
                entity = hit.GetEntity();
                if (entity == null) 
                {
                    error = "You are not looking at a sign.";
                    return false;
                }


                if (entity is Signage signage) paintable = new DiffusionSign(signage);
                else if (entity is PhotoFrame frame) paintable = new DiffusionFrame(frame);
                else if (entity is CarvablePumpkin pumpkin) paintable = new DiffusionPumpkin(pumpkin);
                
                if (paintable != null)
                {

                    if (hit.distance > config.MaxDistance)
                    {
                        error = "You are too far away from the sign.";
                        return false;
                    }
                    return true;
                }
            }
            error = "You are not looking at a valid sign.";
            return false;
        }


        private DiffusionSize GetDiffusionSize(string shortPrefabName)
        {
            return DiffusionSizePerAsset.TryGetValue(shortPrefabName, out var size) ? size : null;
        }



        #endregion

        #region MonoBehaviour & Request Handling

        private class DiffusionBehavior : MonoBehaviour
        {
            public RustDiffusion plugin;

            public void QueueGeneration(BasePlayer player, IDiffusionPaintable paintable, BaseEntity entity, string prompt, uint textureIndex, bool isHanging)
            {
                if (!string.IsNullOrEmpty(plugin.config.LoadingImageUrl))
                {
                    StartCoroutine(ApplyLoadingImage(player, paintable, entity, textureIndex, isHanging));
                }

                if (plugin.config.Provider.Equals("StableDiffusion", StringComparison.OrdinalIgnoreCase))
                {
                    StartCoroutine(GenerateStableDiffusionImage(player, paintable, entity, prompt, textureIndex, isHanging));
                }
                else if (plugin.config.Provider.Equals("OpenAI", StringComparison.OrdinalIgnoreCase))
                {
                    StartCoroutine(GenerateOpenAIImage(player, paintable, entity, prompt, textureIndex, isHanging));
                }
                else if (plugin.config.Provider.Equals("Google", StringComparison.OrdinalIgnoreCase))
                {
                    StartCoroutine(GenerateImageRoutine(player, paintable, entity, prompt, textureIndex, isHanging));
                }
                else
                {
                    if (player != null) player.ChatMessage($"Invalid API Provider configured: {plugin.config.Provider}");
                    plugin.Puts($"Invalid API Provider: {plugin.config.Provider}");
                }
            }

            public void ListAvailableModels(BasePlayer player)
            {
                StartCoroutine(ListModelsRoutine(player));
            }



            private IEnumerator ListModelsRoutine(BasePlayer player)
            {
                string url = $"{plugin.config.ApiBaseUrl}?key={plugin.config.ApiKey}";
                plugin.Puts($"Listing models from: {url}");

                using (UnityWebRequest www = UnityWebRequest.Get(url))
                {
                    yield return www.SendWebRequest();

                    if (www.result != UnityWebRequest.Result.Success)
                    {
                        plugin.Puts($"ListModels Error: {www.error} - {www.downloadHandler.text}");
                        if (player != null) player.ChatMessage($"Error listing models: {www.error}");
                        yield break;
                    }

                    var json = JObject.Parse(www.downloadHandler.text);
                    var models = json["models"];
                    if (models != null)
                    {
                        plugin.Puts("Available Models:");
                        foreach (var model in models)
                        {
                            string name = model["name"]?.ToString();
                            string supported = model["supportedGenerationMethods"]?.ToString();
                            plugin.Puts($"- Name: {name}, Methods: {supported}");
                        }
                        if (player != null) player.ChatMessage("Check server console for model list.");
                    }
                }
            }


            private IEnumerator ApplyLoadingImage(BasePlayer player, IDiffusionPaintable paintable, BaseEntity entity, uint textureIndex, bool isHanging)
            {
                string url = plugin.config.LoadingImageUrl;
                if (string.IsNullOrEmpty(url)) yield break;

                using (UnityWebRequest www = UnityWebRequest.Get(url))
                {
                    yield return www.SendWebRequest();
                    if (www.result == UnityWebRequest.Result.Success)
                    {
                         byte[] imageBytes = www.downloadHandler.data;
                         ApplyImageToSign(player, paintable, entity, imageBytes, textureIndex, isHanging, true); 
                    }
                }
            }

            private IEnumerator GenerateStableDiffusionImage(BasePlayer player, IDiffusionPaintable paintable, BaseEntity entity, string prompt, uint textureIndex, bool isHanging)
            {


                

                string baseUrl = plugin.config.ApiBaseUrl;
                
                if (baseUrl.EndsWith("/")) baseUrl = baseUrl.Substring(0, baseUrl.Length - 1);

                string url = $"{baseUrl}/sdapi/v1/txt2img";
                
                DiffusionSize size = plugin.GetDiffusionSize(entity.ShortPrefabName);
                int width = size != null ? size.ImageWidth : 512;
                int height = size != null ? size.ImageHeight : 512;


                var payload = new
                {
                    prompt = prompt,
                    steps = 20,
                    width = width,
                    height = height,
                    sampler_name = "Euler a",
                    cfg_scale = 7
                };

                string jsonBody = JsonConvert.SerializeObject(payload);

                if (plugin.config.Debug) plugin.Puts($"Sending request to Stable Diffusion: {url}");

                using (UnityWebRequest www = new UnityWebRequest(url, "POST"))
                {
                    byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
                    www.uploadHandler = new UploadHandlerRaw(bodyRaw);
                    www.downloadHandler = new DownloadHandlerBuffer();
                    www.SetRequestHeader("Content-Type", "application/json"); 


                    yield return www.SendWebRequest();

                    if (www.result != UnityWebRequest.Result.Success)
                    {
                        plugin.Puts($"StableDiffusion Error: {www.error} - {www.downloadHandler.text}");
                        if (player != null && player.IsConnected) player.ChatMessage($"Error generating image: Check console.");
                        yield break;
                    }

                    string responseText = www.downloadHandler.text;

                    
                    string b64Image = null;
                    try
                    {
                        JObject json = JObject.Parse(responseText);
                        JArray images = (JArray)json["images"];
                        if (images != null && images.Count > 0)
                        {
                            b64Image = (string)images[0];
                        }
                    }
                    catch (Exception ex)
                    {
                        plugin.Puts($"JSON Parsing Error: {ex.Message}");
                    }

                    if (string.IsNullOrEmpty(b64Image))
                    {
                        if (player != null && player.IsConnected) player.ChatMessage("Failed to parse image from SD response.");
                        yield break;
                    }

                    byte[] imageBytes = Convert.FromBase64String(b64Image);
                    StartCoroutine(SendDiscordLog(player, prompt, null, entity.transform.position, imageBytes));
                    ApplyImageToSign(player, paintable, entity, imageBytes, textureIndex, isHanging);
                }
            }



            private IEnumerator GenerateOpenAIImage(BasePlayer player, IDiffusionPaintable paintable, BaseEntity entity, string prompt, uint textureIndex, bool isHanging)
            {
                string url = plugin.config.ApiBaseUrl;
                if (string.IsNullOrEmpty(url)) url = "https://api.openai.com/v1/images/generations";
                

                string model = !string.IsNullOrEmpty(plugin.config.ModelId) ? plugin.config.ModelId : "gpt-image-1-mini";

                string sizeParam = "1024x1024";
                string qualityParam = "low";

                if (model.Contains("dall-e-2"))
                {

                    DiffusionSize size = plugin.GetDiffusionSize(entity.ShortPrefabName);
                    if (size != null && size.ImageWidth <= 256 && size.ImageHeight <= 256) sizeParam = "256x256";
                    else if (size != null && size.ImageWidth > 512) sizeParam = "1024x1024";
                    else sizeParam = "512x512";
                    

                    qualityParam = null;
                }
                else
                {

                    sizeParam = "1024x1024"; 
                }


                var payloadDict = new Dictionary<string, object>
                {
                    { "model", model },
                    { "prompt", prompt },
                    { "n", 1 },
                    { "size", sizeParam }

                };

                if (!string.IsNullOrEmpty(qualityParam))
                {
                    payloadDict["quality"] = qualityParam;
                }

                string jsonBody = JsonConvert.SerializeObject(payloadDict);

                if (plugin.config.Debug) plugin.Puts($"Sending request to OpenAI ({model}, {sizeParam}, {qualityParam}): {url}");

                using (UnityWebRequest www = new UnityWebRequest(url, "POST"))
                {
                    byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
                    www.uploadHandler = new UploadHandlerRaw(bodyRaw);
                    www.downloadHandler = new DownloadHandlerBuffer();
                    www.SetRequestHeader("Content-Type", "application/json");
                    

                     string apiKey = string.IsNullOrEmpty(plugin.config.ApiKey) || plugin.config.ApiKey == "YOUR_API_KEY_HERE" 
                        ? ""
                        : plugin.config.ApiKey;
                    
                    if (string.IsNullOrEmpty(apiKey))
                    {
                         player.ChatMessage("OpenAI API Key is missing in config.");
                         yield break;
                    }

                    www.SetRequestHeader("Authorization", $"Bearer {apiKey}");

                    yield return www.SendWebRequest();

                    if (www.result != UnityWebRequest.Result.Success)
                    {
                        string errorResponse = www.downloadHandler.text;
                        plugin.Puts($"OpenAI Error: {www.error} - {errorResponse}");


                        if (errorResponse.Contains("moderation_blocked") || errorResponse.Contains("safety_violations"))
                        {
                            if (player != null && player.IsConnected) 
                                player.ChatMessage("Your request was rejected by the safety system (NSFW/Policy Violation).");
                        }
                        else
                        {
                             if (player != null && player.IsConnected) 
                                 player.ChatMessage($"Error generating image: {www.error}");
                        }
                        yield break;
                    }

                    string responseText = www.downloadHandler.text;
                    string imageUrl = null;
                    string b64Image = null;

                    try
                    {
                        JObject json = JObject.Parse(responseText);
                        

                        imageUrl = (string)json.SelectToken("data[0].url");
                        

                        if (string.IsNullOrEmpty(imageUrl))
                        {
                            b64Image = (string)json.SelectToken("data[0].b64_json");
                            

                            if (string.IsNullOrEmpty(b64Image)) b64Image = (string)json.SelectToken("data[0].image");
                            if (string.IsNullOrEmpty(b64Image)) b64Image = (string)json.SelectToken("data[0].base64");
                        }
                    }
                    catch (Exception ex)
                    {
                        plugin.Puts($"JSON Parsing Error: {ex.Message}");
                    }


                    if (!string.IsNullOrEmpty(imageUrl))
                    {

                        StartCoroutine(SendDiscordLog(player, prompt, imageUrl, entity.transform.position, null));
                        using (UnityWebRequest imgRequest = UnityWebRequest.Get(imageUrl))
                        {
                            yield return imgRequest.SendWebRequest();
                            if (imgRequest.result != UnityWebRequest.Result.Success)
                            {
                                plugin.Puts($"Image Download Error: {imgRequest.error}");
                                if (player != null && player.IsConnected) player.ChatMessage($"Failed to download image.");
                                yield break;
                            }
                            
                            byte[] imageBytes = imgRequest.downloadHandler.data;
                            ApplyImageToSign(player, paintable, entity, imageBytes, textureIndex, isHanging);
                        }
                    }
                    else if (!string.IsNullOrEmpty(b64Image))
                    {

                         byte[] imageBytes = Convert.FromBase64String(b64Image);
                         StartCoroutine(SendDiscordLog(player, prompt, null, entity.transform.position, imageBytes));
                         ApplyImageToSign(player, paintable, entity, imageBytes, textureIndex, isHanging);
                    }
                    else
                    {
                        plugin.Puts($"OpenAI Response Parsing Failed. Response did not contain 'url' or 'b64_json'. Full Response: {responseText}");
                        if (player != null && player.IsConnected) player.ChatMessage("Failed to parse image from OpenAI response.");
                        yield break;
                    }
                }
            }

            private IEnumerator GenerateImageRoutine(BasePlayer player, IDiffusionPaintable paintable, BaseEntity entity, string prompt, uint textureIndex, bool isHanging)
            {


                string url = $"{plugin.config.ApiBaseUrl}/{plugin.config.ModelId}:predict?key={plugin.config.ApiKey}";
                


                var payload = new
                {
                    instances = new[] { new { prompt = prompt } },
                    parameters = new { sampleCount = 1 }
                };
                
                string jsonBody = JsonConvert.SerializeObject(payload);

                using (UnityWebRequest www = new UnityWebRequest(url, "POST"))
                {
                    byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
                    www.uploadHandler = new UploadHandlerRaw(bodyRaw);
                    www.downloadHandler = new DownloadHandlerBuffer();
                    www.SetRequestHeader("Content-Type", "application/json");

                    if (plugin.config.Debug) plugin.Puts($"Sending request to {url} with prompt: {prompt}");

                    yield return www.SendWebRequest();

                    if (www.result != UnityWebRequest.Result.Success)
                    {
                        plugin.Puts($"API Error: {www.error} - {www.downloadHandler.text}");
                        if (player != null && player.IsConnected) 
                        {
                            player.ChatMessage($"Error generating image. Check console for details.");
                        }
             

                         if (www.responseCode == 404)
                        {
                             plugin.Puts("404 Error: The Model ID or Endpoint is incorrect. Please check configuration.");
                        }
                        
                        yield break;
                    }

                    string responseText = www.downloadHandler.text;
                    if (plugin.config.Debug) plugin.Puts($"API Response: {responseText}");

                    string b64Image = null;

                    try
                    {
                        JObject json = JObject.Parse(responseText);

                        b64Image = (string)json.SelectToken("predictions[0].bytesBase64Encoded");
                        

                        if (string.IsNullOrEmpty(b64Image))
                        {
                            b64Image = (string)json.SelectToken("predictions[0]");
                        }
                    }
                    catch (Exception ex)
                    {
                        plugin.Puts($"JSON Parsing Error: {ex.Message}");
                    }

                    if (string.IsNullOrEmpty(b64Image))
                    {
                        if (player != null && player.IsConnected) player.ChatMessage("Failed to parse image from API response.");
                        yield break;
                    }

                    byte[] imageBytes = Convert.FromBase64String(b64Image);
                    StartCoroutine(SendDiscordLog(player, prompt, null, entity.transform.position, imageBytes));
                    ApplyImageToSign(player, paintable, entity, imageBytes, textureIndex, isHanging);
                }
            }

            private byte[] ReEncodeImage(byte[] bytes)
            {
                if (bytes == null || bytes.Length == 0) return bytes;
                Texture2D texture = new Texture2D(2, 2);
                if (texture.LoadImage(bytes))
                {
                    byte[] image = texture.EncodeToPNG();
                    UnityEngine.Object.DestroyImmediate(texture);
                    return image;
                }
                UnityEngine.Object.DestroyImmediate(texture);
                return bytes;
            }

            private IEnumerator SendDiscordLog(BasePlayer player, string prompt, string imageUrl, Vector3 pos, byte[] imageBytes)
            {
                if (string.IsNullOrEmpty(plugin.config.DiscordWebhookUrl)) yield break;

                string steamUrl = $"https://steamcommunity.com/profiles/{player.UserIDString}";
                
                var fields = new List<object>
                {
                    new { name = "Player", value = $"[{player.displayName}]({steamUrl})", inline = true },
                    new { name = "Prompt", value = prompt, inline = false },
                    new { name = "Position", value = $"teleportpos {pos.x:F2} {pos.y:F2} {pos.z:F2}", inline = false },
                    new { name = "Server", value = ConVar.Server.hostname, inline = true }
                };

                string embedImageUrl = imageUrl;
                if (string.IsNullOrEmpty(imageUrl) && imageBytes != null)
                {
                    embedImageUrl = "attachment://image.png";
                }

                var embed = new
                {
                    title = "Rust Diffusion Image Generated",
                    color = 3447003,
                    fields = fields,
                    image = !string.IsNullOrEmpty(embedImageUrl) ? new { url = embedImageUrl } : null,
                    footer = new { text = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}" }
                };

                var payload = new
                {
                    embeds = new[] { embed }
                };

                string json = JsonConvert.SerializeObject(payload);

                UnityWebRequest www;
                if (string.IsNullOrEmpty(imageUrl) && imageBytes != null)
                {
                    List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
                    formData.Add(new MultipartFormDataSection("payload_json", json));
                    formData.Add(new MultipartFormFileSection("file", imageBytes, "image.png", "image/png"));
                    www = UnityWebRequest.Post(plugin.config.DiscordWebhookUrl, formData);
                }
                else
                {
                    www = new UnityWebRequest(plugin.config.DiscordWebhookUrl, "POST");
                    byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
                    www.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
                    www.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
                    www.SetRequestHeader("Content-Type", "application/json");
                }

                yield return www.SendWebRequest();

                if (www.result != UnityWebRequest.Result.Success)
                {
                    plugin.Puts($"Discord Log Error: {www.error}");
                }
                www.Dispose();
            }

            private void ApplyImageToSign(BasePlayer player, IDiffusionPaintable paintable, BaseEntity entity, byte[] imageBytes, uint textureIndex, bool isHanging, bool quiet = false)
            {

                if (entity == null || entity.IsDestroyed) return;


                imageBytes = ReEncodeImage(imageBytes);

                DiffusionSize size = plugin.GetDiffusionSize(entity.ShortPrefabName);
                if (size == null)
                {
                    plugin.Puts($"Unknown sign size for {entity.ShortPrefabName}");

                    size = new DiffusionSize(128, 64, 128, 64);

                }

                RotateFlipType rotation = RotateFlipType.RotateNoneFlipNone;
                if (isHanging) rotation = RotateFlipType.RotateNoneFlipX;
                

                 object rotateObj = Interface.Call("GetImageRotation", entity);
                 if (rotateObj is RotateFlipType) rotation = (RotateFlipType)rotateObj;


                byte[] resizedBytes = ResizeImage(imageBytes, size.Width, size.Height, size.ImageWidth, size.ImageHeight, false, rotation);
                
                if (resizedBytes == null)
                {
                    if (player != null) player.ChatMessage("Failed to process/resize image.");
                    return;
                }



                if (paintable.TextureIds == null) return;
                

                uint existingId = paintable.TextureIds[textureIndex];
                if (existingId > 0) FileStorage.server.Remove(existingId, FileStorage.Type.png, paintable.NetId);

                uint newId = FileStorage.server.Store(resizedBytes, FileStorage.Type.png, paintable.NetId, textureIndex);
                paintable.SetImage(textureIndex, newId);
                
                paintable.SendNetworkUpdate();
                



                if (!quiet && player != null && player.IsConnected) player.ChatMessage("Image applied to sign successfully!");
                
                Interface.Oxide.CallHook("OnSignUpdated", entity, player);
            }

            private byte[] ResizeImage(byte[] bytes, int width, int height, int targetWidth, int targetHeight, bool enforceJpeg, RotateFlipType rotation)
            {
                using (MemoryStream originalMs = new MemoryStream(bytes))
                using (MemoryStream resizedMs = new MemoryStream())
                {
                    using (Bitmap original = new Bitmap(originalMs))
                    {
                        if (rotation != RotateFlipType.RotateNoneFlipNone)
                        {
                            original.RotateFlip(rotation);
                        }

                        if (original.Width != targetWidth || original.Height != targetHeight)
                        {
                            using (Bitmap resized = new Bitmap(width, height))
                            using (Graphics g = Graphics.FromImage(resized))
                            {

                                g.CompositingQuality = CompositingQuality.HighQuality;
                                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                                g.SmoothingMode = SmoothingMode.HighQuality;
                                g.PixelOffsetMode = PixelOffsetMode.HighQuality;

                                g.DrawImage(original, new Rectangle(0, 0, targetWidth, targetHeight));
                                

                                Color pixel = Color.FromArgb(UnityEngine.Random.Range(0, 255), UnityEngine.Random.Range(0, 255), UnityEngine.Random.Range(0, 255));
                                resized.SetPixel(width - 1, height - 1, pixel);

                                resized.Save(resizedMs, enforceJpeg ? ImageFormat.Jpeg : ImageFormat.Png);
                            }
                        }
                        else
                        {

                             Color pixel = Color.FromArgb(UnityEngine.Random.Range(0, 255), UnityEngine.Random.Range(0, 255), UnityEngine.Random.Range(0, 255));
                             original.SetPixel(original.Width - 1, original.Height - 1, pixel);
                             original.Save(resizedMs, enforceJpeg ? ImageFormat.Jpeg : ImageFormat.Png);
                        }
                    }
                    return resizedMs.ToArray();
                }
            }
        }

        #endregion

        #region Wrappers
        

        public interface IDiffusionPaintable
        {
            uint[] TextureIds { get; }
            NetworkableId NetId { get; }
            void SetImage(uint textureIndex, uint id);
            void SendNetworkUpdate();
            bool CanUpdate(BasePlayer player);
            void EnsureInitialized();
        }

        public class DiffusionSign : IDiffusionPaintable
        {
            private Signage _sign;
            public DiffusionSign(Signage sign) { _sign = sign; }
            public uint[] TextureIds => _sign.GetTextureCRCs();
            public NetworkableId NetId => _sign.net.ID;
            public void SetImage(uint textureIndex, uint id) { _sign.textureIDs[textureIndex] = id; }
            public void SendNetworkUpdate() { _sign.SendNetworkUpdate(); }
            public bool CanUpdate(BasePlayer player) { return _sign.CanUpdateSign(player); }
            public void EnsureInitialized() { _sign.EnsureInitialized(); }
        }

        public class DiffusionFrame : IDiffusionPaintable
        {
            private PhotoFrame _frame;
            public DiffusionFrame(PhotoFrame frame) { _frame = frame; }
            public uint[] TextureIds => _frame.GetTextureCRCs();
            public NetworkableId NetId => _frame.net.ID;
            public void SetImage(uint textureIndex, uint id) { _frame._overlayTextureCrc = id; }
            public void SendNetworkUpdate() { _frame.SendNetworkUpdate(); }
            public bool CanUpdate(BasePlayer player) { return _frame.CanUpdateSign(player); }
            public void EnsureInitialized() { }
        }

        public class DiffusionPumpkin : IDiffusionPaintable
        {
            private CarvablePumpkin _pumpkin;
            public DiffusionPumpkin(CarvablePumpkin pumpkin) { _pumpkin = pumpkin; }
            public uint[] TextureIds => _pumpkin.textureIDs;
            public NetworkableId NetId => _pumpkin.net.ID;
            public void SetImage(uint textureIndex, uint id) { _pumpkin.textureIDs[textureIndex] = id; }
            public void SendNetworkUpdate() { _pumpkin.SendNetworkUpdate(); }
            public bool CanUpdate(BasePlayer player) { return _pumpkin.CanUpdateSign(player); }
            public void EnsureInitialized() 
            {
                 int size = Mathf.Max(_pumpkin.paintableSources.Length, 1);
                 if (_pumpkin.textureIDs == null || _pumpkin.textureIDs.Length != size)
                     Array.Resize(ref _pumpkin.textureIDs, size);
            }
        }

        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху