From 5104a360c3270b4d70fc6242d9b8b9aa96a80e25 Mon Sep 17 00:00:00 2001 From: Madpeter <57852827+Madpeterz@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:46:33 +0100 Subject: [PATCH] RLV support --- Dockerfile | 4 +- SecondBotEvents/Commands/Self.cs | 6 +- SecondBotEvents/Config/InteractionConfig.cs | 2 +- SecondBotEvents/Config/RlvConfig.cs | 10 - SecondBotEvents/EventsSecondBot.cs | 5 + SecondBotEvents/RLV/RLVAttachmentInfo.cs | 19 + SecondBotEvents/RLV/RLVEventArgs.cs | 34 + SecondBotEvents/RLV/RLVRule.cs | 39 + .../Services/CurrentOutfitFolder.cs | 626 ++++++++++ SecondBotEvents/Services/HomeboundService.cs | 7 +- SecondBotEvents/Services/RLVService.cs | 1092 +++++++++++++++++ 11 files changed, 1828 insertions(+), 16 deletions(-) create mode 100644 SecondBotEvents/RLV/RLVAttachmentInfo.cs create mode 100644 SecondBotEvents/RLV/RLVEventArgs.cs create mode 100644 SecondBotEvents/RLV/RLVRule.cs create mode 100644 SecondBotEvents/Services/CurrentOutfitFolder.cs create mode 100644 SecondBotEvents/Services/RLVService.cs diff --git a/Dockerfile b/Dockerfile index 1c5ecc2..2a43216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -89,7 +89,9 @@ ENV basic_Username='' \ events_HideStatusOutput='false' \ homebound_HideStatusOutput='false' \ http_HideStatusOutput='false' \ - interaction_HideStatusOutput='false' + interaction_HideStatusOutput='false' \ + rlv_Enabled='false' \ + rlv_HideStatusOutput='false' EXPOSE 80 diff --git a/SecondBotEvents/Commands/Self.cs b/SecondBotEvents/Commands/Self.cs index 3091edd..3c0dde4 100644 --- a/SecondBotEvents/Commands/Self.cs +++ b/SecondBotEvents/Commands/Self.cs @@ -86,17 +86,17 @@ namespace SecondBotEvents.Commands [About("Makes the bot sit on the ground or on a object if it can see it")] [ReturnHints("ok")] [ReturnHintsFailure("Invaild object UUID")] - [ArgHints("target", "ground or a object UUID")] + [ArgHints("target", "a object UUID anything or nothing will also work")] public object Sit(string target) { - if (target == "ground") + if ((target == "ground") || (target == UUID.Zero.ToString())) { GetClient().Self.SitOnGround(); return BasicReply("ok", new [] { target }); } if(UUID.TryParse(target,out UUID objectuuid) == false) { - return Failure("Invaild object UUID", new [] { target }); + return Failure("Invaild object uuid and not ground"); } GetClient().Self.RequestSit(objectuuid, Vector3.Zero); return BasicReply("ok", new [] { target }); diff --git a/SecondBotEvents/Config/InteractionConfig.cs b/SecondBotEvents/Config/InteractionConfig.cs index 0f10f13..9847317 100644 --- a/SecondBotEvents/Config/InteractionConfig.cs +++ b/SecondBotEvents/Config/InteractionConfig.cs @@ -27,7 +27,7 @@ namespace SecondBotEvents.Config public bool GetAcceptTeleports() { - return ReadSettingAsBool("AcceptTeleports", true); + return ReadSettingAsBool("AcceptTeleports", false); } public bool GetAcceptGroupInvites() diff --git a/SecondBotEvents/Config/RlvConfig.cs b/SecondBotEvents/Config/RlvConfig.cs index a96fefb..14c97b1 100644 --- a/SecondBotEvents/Config/RlvConfig.cs +++ b/SecondBotEvents/Config/RlvConfig.cs @@ -11,18 +11,8 @@ namespace SecondBotEvents.Config { filename = "rlv"; settings.Add("Enabled"); - settings.Add("AllowRlvViaIm"); - settings.Add("AllowRlvViaObjectOwnerSay"); settings.Add("HideStatusOutput"); } - public bool GetAllowRlvViaIm() - { - return ReadSettingAsBool("AllowRlvViaIm", false); - } - public bool GetAllowRlvViaObjectOwnerSay() - { - return ReadSettingAsBool("AllowRlvViaObjectOwnerSay", false); - } public bool GetEnabled() { diff --git a/SecondBotEvents/EventsSecondBot.cs b/SecondBotEvents/EventsSecondBot.cs index ce113b8..f08c41b 100644 --- a/SecondBotEvents/EventsSecondBot.cs +++ b/SecondBotEvents/EventsSecondBot.cs @@ -39,6 +39,9 @@ namespace SecondBotEvents public EventsService EventsService { get { return (EventsService)GetService("EventsService"); } } public DialogService DialogService { get { return (DialogService)GetService("DialogService"); } } + public CurrentOutfitFolder CurrentOutfitFolder { get { return (CurrentOutfitFolder)GetService("CurrentOutfitFolder"); } } + public RLVService RLV { get { return (RLVService)GetService("RLVService"); } } + public CustomCommandsService CustomCommandsService { get { return (CustomCommandsService)GetService("CustomCommandsService"); } } @@ -145,6 +148,8 @@ namespace SecondBotEvents lastStatus = new Dictionary(); RegisterService("BotClientService", false); RegisterService("DataStoreService"); + RegisterService("RLVService"); + RegisterService("CurrentOutfitFolder"); RegisterService("HttpService"); RegisterService("CommandsService"); RegisterService("DiscordService"); diff --git a/SecondBotEvents/RLV/RLVAttachmentInfo.cs b/SecondBotEvents/RLV/RLVAttachmentInfo.cs new file mode 100644 index 0000000..c250a6b --- /dev/null +++ b/SecondBotEvents/RLV/RLVAttachmentInfo.cs @@ -0,0 +1,19 @@ +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SecondBotEvents.RLV +{ + public class AttachmentInfo + { + public Primitive Prim; + public InventoryItem Item; + public UUID InventoryID; + public UUID PrimID; + public bool MarkedAttached; + public AttachmentPoint Point => Prim != null ? Prim.PrimData.AttachmentPoint : AttachmentPoint.Default; + } +} diff --git a/SecondBotEvents/RLV/RLVEventArgs.cs b/SecondBotEvents/RLV/RLVEventArgs.cs new file mode 100644 index 0000000..92124df --- /dev/null +++ b/SecondBotEvents/RLV/RLVEventArgs.cs @@ -0,0 +1,34 @@ +/** + * Radegast Metaverse Client + * Copyright(c) 2009-2014, Radegast Development Team + * Copyright(c) 2016-2020, Sjofn, LLC + * All rights reserved. + * + * Radegast is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program.If not, see. + */ + +using System; + +namespace SecondBotEvents.RLV +{ + public class RLVEventArgs : EventArgs + { + public RLVRule Rule { set; get; } + + public RLVEventArgs(RLVRule rule) + { + Rule = rule; + } + } +} \ No newline at end of file diff --git a/SecondBotEvents/RLV/RLVRule.cs b/SecondBotEvents/RLV/RLVRule.cs new file mode 100644 index 0000000..3b8907c --- /dev/null +++ b/SecondBotEvents/RLV/RLVRule.cs @@ -0,0 +1,39 @@ +/** + * Radegast Metaverse Client + * Copyright(c) 2009-2014, Radegast Development Team + * Copyright(c) 2016-2020, Sjofn, LLC + * All rights reserved. + * + * Radegast is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program.If not, see. + */ + +using OpenMetaverse; + +namespace SecondBotEvents.RLV +{ + public class RLVRule + { + public string Behaviour { set; get; } + public string Option { set; get; } + public string Param { set; get; } + public UUID Sender { set; get; } + public string SenderName { set; get; } + + public override string ToString() + { + return string.Format("{0}: {1}:{2}={3} [{4}]", SenderName, Behaviour, Option, Param, Sender); + } + } +} + diff --git a/SecondBotEvents/Services/CurrentOutfitFolder.cs b/SecondBotEvents/Services/CurrentOutfitFolder.cs new file mode 100644 index 0000000..f2d5879 --- /dev/null +++ b/SecondBotEvents/Services/CurrentOutfitFolder.cs @@ -0,0 +1,626 @@ +using OpenMetaverse; +using SecondBotEvents.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using static SecondBotEvents.Services.RLVService; + + +namespace SecondBotEvents.Services +{ + public class CurrentOutfitFolder : BotServices + { + #region Fields + private bool InitiCOF = false; + private bool AppearanceSent = false; + private bool COFReady = false; + private bool InitialUpdateDone = false; + public Dictionary Content = new Dictionary(); + public InventoryFolder COF; + protected bool botConnected = false; + + #endregion Fields + + #region Construction and disposal + public CurrentOutfitFolder(EventsSecondBot setMaster) : base(setMaster) + { + } + + public override string Status() + { + if (botConnected == false) + { + return "Waiting for bot"; + } + return "Active"; + } + + public override void Start() + { + Stop(); + running = true; + master.BotClientNoticeEvent += BotClientRestart; + LogFormater.Info("COF inventor [Starting]"); + } + + public override void Stop() + { + if (running == true) + { + LogFormater.Info("COF inventor [Stopping]"); + } + running = false; + } + + protected void BotClientRestart(object o, BotClientNotice e) + { + LogFormater.Info("COF inventory [Attached to new client]"); + GetClient().Network.LoggedOut += BotLoggedOut; + GetClient().Network.SimConnected += BotLoggedIn; + } + + protected void BotLoggedOut(object o, LoggedOutEventArgs e) + { + GetClient().Network.SimConnected += BotLoggedIn; + botConnected = false; + LogFormater.Info("COF inventory [Logged out]"); + UnregisterClientEvents(); + } + + protected void BotLoggedIn(object o, SimConnectedEventArgs e) + { + GetClient().Network.SimConnected -= BotLoggedIn; + LogFormater.Info("COF inventory [starting]"); + RegisterClientEvents(); + } + #endregion Construction and disposal + + #region Event handling + private void RegisterClientEvents() + { + GetClient().Network.EventQueueRunning += Network_EventQueueRunning; + GetClient().Inventory.FolderUpdated += Inventory_FolderUpdated; + GetClient().Inventory.ItemReceived += Inventory_ItemReceived; + GetClient().Appearance.AppearanceSet += Appearance_AppearanceSet; + GetClient().Objects.KillObject += Objects_KillObject; + } + + private void UnregisterClientEvents() + { + GetClient().Network.EventQueueRunning -= Network_EventQueueRunning; + GetClient().Inventory.FolderUpdated -= Inventory_FolderUpdated; + GetClient().Inventory.ItemReceived -= Inventory_ItemReceived; + GetClient().Appearance.AppearanceSet -= Appearance_AppearanceSet; + GetClient().Objects.KillObject -= Objects_KillObject; + lock (Content) Content.Clear(); + InitiCOF = false; + AppearanceSent = false; + COFReady = false; + InitialUpdateDone = false; + } + + private void Appearance_AppearanceSet(object sender, AppearanceSetEventArgs e) + { + AppearanceSent = true; + if (COFReady) + { + InitialUpdate(); + } + } + + private void Inventory_ItemReceived(object sender, ItemReceivedEventArgs e) + { + var links = ContentLinks(); + bool partOfCOF = links.Any(cofItem => cofItem.AssetUUID == e.Item.UUID); + + if (partOfCOF) + { + lock (Content) + { + Content[e.Item.UUID] = e.Item; + } + } + + if (Content.Count != links.Count) return; + COFReady = true; + if (AppearanceSent) + { + InitialUpdate(); + } + lock (Content) + { + foreach (var lk in from link in Content.Values + where link.InventoryType == InventoryType.Wearable + select (InventoryWearable)link into w + select links.Find(l => l.AssetUUID == w.UUID)) + { } + } + } + + private readonly object FolderSync = new object(); + + private void Inventory_FolderUpdated(object sender, FolderUpdatedEventArgs e) + { + if (COF == null) return; + + if (e.FolderID == COF.UUID && e.Success) + { + COF = (InventoryFolder)GetClient().Inventory.Store[COF.UUID]; + lock (FolderSync) + { + lock (Content) Content.Clear(); + + + List items = new List(); + List owners = new List(); + + foreach (var link in ContentLinks()) + { + //if (Client.Inventory.Store.Contains(link.AssetUUID)) + //{ + // continue; + //} + items.Add(link.AssetUUID); + owners.Add(GetClient().Self.AgentID); + } + + if (items.Count > 0) + { + GetClient().Inventory.RequestFetchInventory(items, owners); + } + } + } + } + + private void Objects_KillObject(object sender, KillObjectEventArgs e) + { + if (GetClient().Network.CurrentSim != e.Simulator) return; + + Primitive prim = null; + if (GetClient().Network.CurrentSim.ObjectsPrimitives.TryGetValue(e.ObjectLocalID, out prim)) + { + UUID invItem = GetAttachmentItem(prim); + if (invItem != UUID.Zero) + { + RemoveLink(invItem); + } + } + } + + private void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e) + { + if (e.Simulator == GetClient().Network.CurrentSim && !InitiCOF) + { + InitiCOF = true; + InitCOF(); + } + } + #endregion Event handling + + #region Private methods + + private void RequestDescendants(UUID folderID) + { + GetClient().Inventory.RequestFolderContents(folderID, GetClient().Self.AgentID, true, true, InventorySortOrder.ByDate); + } + + private void InitCOF() + { + var rootContent = GetClient().Inventory.Store.GetContents(GetClient().Inventory.Store.RootFolder.UUID); + foreach (var baseItem in rootContent) + { + if (baseItem is InventoryFolder folder && folder.PreferredType == FolderType.CurrentOutfit) + { + COF = folder; + break; + } + } + + if (COF == null) + { + CreateCOF(); + } + else + { + RequestDescendants(COF.UUID); + } + } + + private void CreateCOF() + { + UUID cofID = GetClient().Inventory.CreateFolder(GetClient().Inventory.Store.RootFolder.UUID, "Current Outfit", FolderType.CurrentOutfit); + if (GetClient().Inventory.Store.Items.ContainsKey(cofID) && GetClient().Inventory.Store.Items[cofID].Data is InventoryFolder folder) + { + COF = folder; + COFReady = true; + if (AppearanceSent) + { + InitialUpdate(); + } + } + } + + private void InitialUpdate() + { + if (InitialUpdateDone) return; + InitialUpdateDone = true; + lock (Content) + { + var myAtt = GetClient().Network.CurrentSim.ObjectsPrimitives.FindAll(p => p.ParentID == GetClient().Self.LocalID); + + foreach (var item in Content.Values + .Where(item => item is InventoryObject || item is InventoryAttachment) + .Where(item => !IsAttached(myAtt, item))) + { + GetClient().Appearance.Attach(item, AttachmentPoint.Default, false); + } + } + } + #endregion Private methods + + #region Public methods + /// + /// Get COF contents + /// + /// List if InventoryItems that can be part of appearance (attachments, wearables) + public List ContentLinks() + { + var ret = new List(); + if (COF == null) return ret; + + GetClient().Inventory.Store.GetContents(COF) + .FindAll(b => CanBeWorn(b) && ((InventoryItem)b).AssetType == AssetType.Link) + .ForEach(item => ret.Add((InventoryItem)item)); + + return ret; + } + + /// + /// Get inventory ID of a prim + /// + /// Prim to check + /// Inventory ID of the object. UUID.Zero if not found + public static UUID GetAttachmentItem(Primitive prim) + { + if (prim.NameValues == null) return UUID.Zero; + + for (var i = 0; i < prim.NameValues.Length; i++) + { + if (prim.NameValues[i].Name == "AttachItemID") + { + return (UUID)prim.NameValues[i].Value.ToString(); + } + } + return UUID.Zero; + } + + /// + /// Is an inventory item currently attached + /// + /// List of root prims that are attached to our avatar + /// Inventory item to check + /// True if the inventory item is attached to avatar + public static bool IsAttached(List attachments, InventoryItem item) + { + return attachments.Any(prim => GetAttachmentItem(prim) == item.UUID); + } + + /// + /// Checks if inventory item of Wearable type is worn + /// + /// Current outfit + /// Item to check + /// True if the item is worn + public static bool IsWorn(List currentlyWorn, InventoryItem item) + { + return currentlyWorn.Any(worn => worn.ItemID == item.UUID); + } + + /// + /// Can this inventory type be worn + /// + /// Item to check + /// True if the inventory item can be worn + public static bool CanBeWorn(InventoryBase item) + { + return item is InventoryWearable || item is InventoryAttachment || item is InventoryObject; + } + + /// + /// Attach an inventory item + /// + /// Item to be attached + /// Attachment point + /// Replace existing attachment at that point first? + public void Attach(InventoryItem item, AttachmentPoint point, bool replace) + { + GetClient().Appearance.Attach(item, point, replace); + AddLink(item); + } + + /// + /// Creates a new COF link + /// + /// Original item to be linked from COF + public void AddLink(InventoryItem item) + { + if (item.InventoryType == InventoryType.Wearable && !IsBodyPart(item)) + { + var w = (InventoryWearable)item; + int layer = 0; + string desc = $"@{(int)w.WearableType}{layer:00}"; + AddLink(item, desc); + } + else + { + AddLink(item, string.Empty); + } + } + + /// + /// Creates a new COF link + /// + /// Original item to be linked from COF + /// Description for the link + public void AddLink(InventoryItem item, string newDescription) + { + if (COF == null) return; + + bool linkExists = null != ContentLinks().Find(itemLink => itemLink.AssetUUID == item.UUID); + + if (!linkExists) + { + GetClient().Inventory.CreateLink(COF.UUID, item.UUID, item.Name, newDescription, + AssetType.Link, item.InventoryType, UUID.Random(), + (success, newItem) => + { + if (success) + { + GetClient().Inventory.RequestFetchInventory(newItem.UUID, newItem.OwnerID); + } + }); + } + } + + /// + /// Remove a link to specified inventory item + /// + /// ID of the target inventory item for which we want link to be removed + public void RemoveLink(UUID itemID) + { + RemoveLink(new List(1) { itemID }); + } + + /// + /// Remove a link to specified inventory item + /// + /// List of IDs of the target inventory item for which we want link to be removed + public void RemoveLink(List itemIDs) + { + if (COF == null) return; + + foreach (var links in itemIDs.Select(itemID => ContentLinks() + .FindAll(itemLink => itemLink.AssetUUID == itemID))) + { + links.ForEach(item => GetClient().Inventory.RemoveItem(item.UUID)); + } + } + + /// + /// Remove attachment + /// + /// >Inventory item to be detached + public void Detach(InventoryItem item) + { + var realItem = RealInventoryItem(item); + if (!master.RLV.AllowDetach(realItem)) return; + + GetClient().Appearance.Detach(item); + RemoveLink(item.UUID); + } + + public List GetWornAt(WearableType type) + { + var ret = new List(); + ContentLinks().ForEach(link => + { + var item = RealInventoryItem(link); + if (!(item is InventoryWearable wearable)) return; + + if (wearable.WearableType == type) + { + ret.Add(wearable); + } + }); + + return ret; + } + + /// + /// Resolves inventory links and returns a real inventory item that + /// the link is pointing to + /// + /// + /// + public InventoryItem RealInventoryItem(InventoryItem item) + { + if (item.IsLink() && GetClient().Inventory.Store.Contains(item.AssetUUID) + && GetClient().Inventory.Store[item.AssetUUID] is InventoryItem invItem) + { + return invItem; + } + + return item; + } + + /// + /// Replaces the current outfit and updates COF links accordingly + /// + /// List of new wearables and attachments that comprise the new outfit + public void ReplaceOutfit(List newOutfit) + { + // Resolve inventory links + var outfit = newOutfit.Select(RealInventoryItem).ToList(); + + // Remove links to all exiting items + var toRemove = new List(); + ContentLinks().ForEach(item => + { + if (IsBodyPart(item)) + { + WearableType linkType = ((InventoryWearable)RealInventoryItem(item)).WearableType; + bool hasBodyPart = newOutfit.Select(RealInventoryItem).Where(IsBodyPart).Any(newItem => + ((InventoryWearable)newItem).WearableType == linkType); + + if (hasBodyPart) + { + toRemove.Add(item.UUID); + } + } + else + { + toRemove.Add(item.UUID); + } + }); + + foreach (var item in toRemove) + { + GetClient().Inventory.RemoveItem(item); + } + + // Add links to new items + var newItems = outfit.FindAll(CanBeWorn); + foreach (var item in newItems) + { + AddLink(item); + } + + GetClient().Appearance.ReplaceOutfit(outfit, false); + ThreadPool.QueueUserWorkItem(sync => + { + Thread.Sleep(2000); + GetClient().Appearance.RequestSetAppearance(true); + }); + } + + /// + /// Add items to current outfit + /// + /// Item to add + /// Should existing wearable of the same type be removed + public void AddToOutfit(InventoryItem item, bool replace) + { + AddToOutfit(new List(1) { item }, replace); + } + + /// + /// Add items to current outfit + /// + /// List of items to add + /// Should existing wearable of the same type be removed + public void AddToOutfit(List items, bool replace) + { + var current = ContentLinks(); + var toRemove = new List(); + + // Resolve inventory links and remove wearables of the same type from COF + var outfit = new List(); + + foreach (var item in items) + { + var realItem = RealInventoryItem(item); + if (replace && realItem is InventoryWearable wearable) + { + foreach (var link in current) + { + var currentItem = RealInventoryItem(link); + if (link.AssetUUID == item.UUID) + { + toRemove.Add(link.UUID); + } + else + { + var w = currentItem as InventoryWearable; + if (w?.WearableType == wearable.WearableType) + { + toRemove.Add(link.UUID); + } + } + } + } + + outfit.Add(realItem); + } + + foreach (var item in toRemove) + { + GetClient().Inventory.RemoveItem(item); + } + + // Add links to new items + var newItems = outfit.FindAll(CanBeWorn); + foreach (var item in newItems) + { + AddLink(item); + } + + GetClient().Appearance.AddToOutfit(outfit, replace); + ThreadPool.QueueUserWorkItem(sync => + { + Thread.Sleep(2000); + GetClient().Appearance.RequestSetAppearance(true); + }); + } + + /// + /// Remove an item from the current outfit + /// + /// Item to remove + public void RemoveFromOutfit(InventoryItem item) + { + RemoveFromOutfit(new List(1) { item }); + } + + /// + /// Remove specified items from the current outfit + /// + /// List of items to remove + public void RemoveFromOutfit(List items) + { + // Resolve inventory links + var outfit = items.Select(RealInventoryItem).Where(realItem => master.RLV.AllowDetach(realItem)).ToList(); + + // Remove links to all items that were removed + var toRemove = outfit.FindAll(item => CanBeWorn(item) && !IsBodyPart(item)).Select(item => item.UUID).ToList(); + RemoveLink(toRemove); + + GetClient().Appearance.RemoveFromOutfit(outfit); + } + + public bool IsBodyPart(InventoryItem item) + { + var realItem = RealInventoryItem(item); + if (!(realItem is InventoryWearable wearable)) return false; + + var t = wearable.WearableType; + return t == WearableType.Shape || + t == WearableType.Skin || + t == WearableType.Eyes || + t == WearableType.Hair; + } + + /// + /// Force rebaking textures + /// + public void RebakeTextures() + { + GetClient().Appearance.RequestSetAppearance(true); + } + + #endregion Public methods + } +} diff --git a/SecondBotEvents/Services/HomeboundService.cs b/SecondBotEvents/Services/HomeboundService.cs index 8d475ee..d73755c 100644 --- a/SecondBotEvents/Services/HomeboundService.cs +++ b/SecondBotEvents/Services/HomeboundService.cs @@ -75,7 +75,6 @@ namespace SecondBotEvents.Services } protected void Tick() { - long dif = 0; if (evacWanted == true) { BotAlertMessage(null, new AlertMessageEventArgs("restart", "999", new OpenMetaverse.StructuredData.OSDMap())); @@ -327,6 +326,12 @@ namespace SecondBotEvents.Services return; } Stop(); + if(master.RLV.Enabled == true) + { + myConfig.setEnabled(false); + LogFormater.Info("Homebound Service [Disabled RLV enabled]"); + return; + } running = true; master.BotClientNoticeEvent += BotClientRestart; LogFormater.Info("Homebound Service [Starting]"); diff --git a/SecondBotEvents/Services/RLVService.cs b/SecondBotEvents/Services/RLVService.cs new file mode 100644 index 0000000..a3a04cf --- /dev/null +++ b/SecondBotEvents/Services/RLVService.cs @@ -0,0 +1,1092 @@ +using OpenMetaverse; +using SecondBotEvents.Config; +using System.Collections.Generic; +using Newtonsoft.Json; +using RestSharp; +using OpenMetaverse.StructuredData; +using System.Linq; +using System.Text.RegularExpressions; +using System.Text; +using System; +using SecondBotEvents.RLV; + +namespace SecondBotEvents.Services +{ + public class RLVService : BotServices + { + protected RlvConfig myConfig; + protected bool botConnected = false; + protected CurrentOutfitFolder CurrentOutfitFolder = null; + + public RLVService(EventsSecondBot setMaster) : base(setMaster) + { + myConfig = new RlvConfig(master.fromEnv, master.fromFolder); + this.instance = setMaster; + if (myConfig.GetEnabled() == false) + { + return; + } + } + public override string Status() + { + if (myConfig == null) + { + return "No Config"; + } + else if (myConfig.GetHideStatusOutput() == true) + { + return "hidden"; + } + else if (myConfig.GetEnabled() == false) + { + return "- Not requested -"; + } + else if (botConnected == false) + { + return "Waiting for bot"; + } + return "Active"; + } + + protected void BotClientRestart(object o, BotClientNotice e) + { + LogFormater.Info("RLV service [Attached to new client]"); + GetClient().Network.LoggedOut += BotLoggedOut; + GetClient().Network.SimConnected += BotLoggedIn; + } + + protected void BotLoggedOut(object o, LoggedOutEventArgs e) + { + GetClient().Network.SimConnected += BotLoggedIn; + botConnected = false; + LogFormater.Info("RLV service [Logged out]"); + removeEvents(); + } + + protected void BotLoggedIn(object o, SimConnectedEventArgs e) + { + GetClient().Network.SimConnected -= BotLoggedIn; + GetClient().Self.ChatFromSimulator += BotChatEvents; + CurrentOutfitFolder = master.CurrentOutfitFolder; + botConnected = true; + // attach events + RLVWearables = new List(); + RLVAttachments = new List + { + new RLVAttachment() {Name = "none", Point = AttachmentPoint.Default} + }; + foreach (WearableType wear in Enum.GetValues(typeof(WearableType))) + { + if (wear.Equals(WearableType.Invalid)) continue; + string wearstr = Utils.EnumToText(wear); + RLVWearables.Add(new RLVWearable() { Name = wearstr.ToLower(), Type = wear }); + } + foreach (AttachmentPoint pt in Enum.GetValues(typeof(AttachmentPoint))) + { + string ptstr = Utils.EnumToText(pt); + if (pt.Equals(AttachmentPoint.Default) || ptstr.StartsWith("HUD")) continue; + RLVAttachments.Add(new RLVAttachment { Name = ptstr.ToLower(), Point = pt }); + } + } + + protected void removeEvents() + { + botConnected = false; + if (GetClient() != null) + { + // remove events + GetClient().Self.ChatFromSimulator -= BotChatEvents; + } + } + + + public override void Start() + { + if (myConfig.GetEnabled() == false) + { + LogFormater.Info("Events service [Disabled]"); + return; + } + Stop(); + running = true; + master.BotClientNoticeEvent += BotClientRestart; + } + + public override void Stop() + { + if (myConfig.GetEnabled() == false) + { + return; + } + running = false; + master.BotClientNoticeEvent -= BotClientRestart; + removeEvents(); + } + + #region Events + /// The event subscribers. null if no subcribers + private EventHandler m_RLVRuleChanged; + + /// Raises the RLVRuleChanged event + /// An RLVRuleChangedEventArgs object containing the + /// data returned from the data server + protected virtual void OnRLVRuleChanged(RLVEventArgs e) + { + EventHandler handler = m_RLVRuleChanged; + try + { + handler?.Invoke(this, e); + } + catch (Exception) { } + + } + + /// Thread sync lock object + private readonly object m_RLVRuleChangedLock = new object(); + + /// Triggered when an RLVRuleChangedUpdate packet is received, + /// telling us what our avatar is currently wearing + /// request. + public event EventHandler RLVRuleChanged + { + add { lock (m_RLVRuleChangedLock) { m_RLVRuleChanged += value; } } + remove { lock (m_RLVRuleChangedLock) { m_RLVRuleChanged -= value; } } + } + #endregion + + #region Helper classes, methods, structs and enums + public struct RLVWearable + { + public string Name { get; set; } + public WearableType Type { get; set; } + } + + public struct RLVAttachment + { + public string Name { get; set; } + public AttachmentPoint Point { get; set; } + } + + public List RLVWearables; + public List RLVAttachments; + + public WearableType WearableFromString(string type) + { + var found = RLVWearables.FindAll(w => w.Name == type); + return found.Count == 1 ? found[0].Type : WearableType.Invalid; + } + + public AttachmentPoint AttachmentPointFromString(string point) + { + var found = RLVAttachments.FindAll(a => a.Name == point); + return found.Count == 1 ? found[0].Point : AttachmentPoint.Default; + } + + #endregion Helper classes, structs and enums + + public bool Enabled + { + get + { + return myConfig.GetEnabled(); + } + + set + { + myConfig.setEnabled(value); + if (value) + StartTimer(); + else + StopTimer(); + } + } + + GridClient client => instance.BotClient.GetClient(); + EventsSecondBot instance = null; + readonly Regex rlv_regex = new Regex(@"(?[^:=]+)(:(?