RLV support
This commit is contained in:
parent
cdfc32fdaf
commit
5104a360c3
11 changed files with 1828 additions and 16 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace SecondBotEvents.Config
|
|||
|
||||
public bool GetAcceptTeleports()
|
||||
{
|
||||
return ReadSettingAsBool("AcceptTeleports", true);
|
||||
return ReadSettingAsBool("AcceptTeleports", false);
|
||||
}
|
||||
|
||||
public bool GetAcceptGroupInvites()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<string, string>();
|
||||
RegisterService("BotClientService", false);
|
||||
RegisterService("DataStoreService");
|
||||
RegisterService("RLVService");
|
||||
RegisterService("CurrentOutfitFolder");
|
||||
RegisterService("HttpService");
|
||||
RegisterService("CommandsService");
|
||||
RegisterService("DiscordService");
|
||||
|
|
|
|||
19
SecondBotEvents/RLV/RLVAttachmentInfo.cs
Normal file
19
SecondBotEvents/RLV/RLVAttachmentInfo.cs
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
34
SecondBotEvents/RLV/RLVEventArgs.cs
Normal file
34
SecondBotEvents/RLV/RLVEventArgs.cs
Normal file
|
|
@ -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<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SecondBotEvents.RLV
|
||||
{
|
||||
public class RLVEventArgs : EventArgs
|
||||
{
|
||||
public RLVRule Rule { set; get; }
|
||||
|
||||
public RLVEventArgs(RLVRule rule)
|
||||
{
|
||||
Rule = rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
SecondBotEvents/RLV/RLVRule.cs
Normal file
39
SecondBotEvents/RLV/RLVRule.cs
Normal file
|
|
@ -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<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
626
SecondBotEvents/Services/CurrentOutfitFolder.cs
Normal file
626
SecondBotEvents/Services/CurrentOutfitFolder.cs
Normal file
|
|
@ -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<UUID, InventoryItem> Content = new Dictionary<UUID, InventoryItem>();
|
||||
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<UUID> items = new List<UUID>();
|
||||
List<UUID> owners = new List<UUID>();
|
||||
|
||||
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
|
||||
/// <summary>
|
||||
/// Get COF contents
|
||||
/// </summary>
|
||||
/// <returns>List if InventoryItems that can be part of appearance (attachments, wearables)</returns>
|
||||
public List<InventoryItem> ContentLinks()
|
||||
{
|
||||
var ret = new List<InventoryItem>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get inventory ID of a prim
|
||||
/// </summary>
|
||||
/// <param name="prim">Prim to check</param>
|
||||
/// <returns>Inventory ID of the object. UUID.Zero if not found</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is an inventory item currently attached
|
||||
/// </summary>
|
||||
/// <param name="attachments">List of root prims that are attached to our avatar</param>
|
||||
/// <param name="item">Inventory item to check</param>
|
||||
/// <returns>True if the inventory item is attached to avatar</returns>
|
||||
public static bool IsAttached(List<Primitive> attachments, InventoryItem item)
|
||||
{
|
||||
return attachments.Any(prim => GetAttachmentItem(prim) == item.UUID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if inventory item of Wearable type is worn
|
||||
/// </summary>
|
||||
/// <param name="currentlyWorn">Current outfit</param>
|
||||
/// <param name="item">Item to check</param>
|
||||
/// <returns>True if the item is worn</returns>
|
||||
public static bool IsWorn(List<AppearanceManager.WearableData> currentlyWorn, InventoryItem item)
|
||||
{
|
||||
return currentlyWorn.Any(worn => worn.ItemID == item.UUID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can this inventory type be worn
|
||||
/// </summary>
|
||||
/// <param name="item">Item to check</param>
|
||||
/// <returns>True if the inventory item can be worn</returns>
|
||||
public static bool CanBeWorn(InventoryBase item)
|
||||
{
|
||||
return item is InventoryWearable || item is InventoryAttachment || item is InventoryObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attach an inventory item
|
||||
/// </summary>
|
||||
/// <param name="item">Item to be attached</param>
|
||||
/// <param name="point">Attachment point</param>
|
||||
/// <param name="replace">Replace existing attachment at that point first?</param>
|
||||
public void Attach(InventoryItem item, AttachmentPoint point, bool replace)
|
||||
{
|
||||
GetClient().Appearance.Attach(item, point, replace);
|
||||
AddLink(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new COF link
|
||||
/// </summary>
|
||||
/// <param name="item">Original item to be linked from COF</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new COF link
|
||||
/// </summary>
|
||||
/// <param name="item">Original item to be linked from COF</param>
|
||||
/// <param name="newDescription">Description for the link</param>
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a link to specified inventory item
|
||||
/// </summary>
|
||||
/// <param name="itemID">ID of the target inventory item for which we want link to be removed</param>
|
||||
public void RemoveLink(UUID itemID)
|
||||
{
|
||||
RemoveLink(new List<UUID>(1) { itemID });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a link to specified inventory item
|
||||
/// </summary>
|
||||
/// <param name="itemIDs">List of IDs of the target inventory item for which we want link to be removed</param>
|
||||
public void RemoveLink(List<UUID> 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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove attachment
|
||||
/// </summary>
|
||||
/// <param name="item">>Inventory item to be detached</param>
|
||||
public void Detach(InventoryItem item)
|
||||
{
|
||||
var realItem = RealInventoryItem(item);
|
||||
if (!master.RLV.AllowDetach(realItem)) return;
|
||||
|
||||
GetClient().Appearance.Detach(item);
|
||||
RemoveLink(item.UUID);
|
||||
}
|
||||
|
||||
public List<InventoryItem> GetWornAt(WearableType type)
|
||||
{
|
||||
var ret = new List<InventoryItem>();
|
||||
ContentLinks().ForEach(link =>
|
||||
{
|
||||
var item = RealInventoryItem(link);
|
||||
if (!(item is InventoryWearable wearable)) return;
|
||||
|
||||
if (wearable.WearableType == type)
|
||||
{
|
||||
ret.Add(wearable);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves inventory links and returns a real inventory item that
|
||||
/// the link is pointing to
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the current outfit and updates COF links accordingly
|
||||
/// </summary>
|
||||
/// <param name="newOutfit">List of new wearables and attachments that comprise the new outfit</param>
|
||||
public void ReplaceOutfit(List<InventoryItem> newOutfit)
|
||||
{
|
||||
// Resolve inventory links
|
||||
var outfit = newOutfit.Select(RealInventoryItem).ToList();
|
||||
|
||||
// Remove links to all exiting items
|
||||
var toRemove = new List<UUID>();
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add items to current outfit
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add</param>
|
||||
/// <param name="replace">Should existing wearable of the same type be removed</param>
|
||||
public void AddToOutfit(InventoryItem item, bool replace)
|
||||
{
|
||||
AddToOutfit(new List<InventoryItem>(1) { item }, replace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add items to current outfit
|
||||
/// </summary>
|
||||
/// <param name="items">List of items to add</param>
|
||||
/// <param name="replace">Should existing wearable of the same type be removed</param>
|
||||
public void AddToOutfit(List<InventoryItem> items, bool replace)
|
||||
{
|
||||
var current = ContentLinks();
|
||||
var toRemove = new List<UUID>();
|
||||
|
||||
// Resolve inventory links and remove wearables of the same type from COF
|
||||
var outfit = new List<InventoryItem>();
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove an item from the current outfit
|
||||
/// </summary>
|
||||
/// <param name="item">Item to remove</param>
|
||||
public void RemoveFromOutfit(InventoryItem item)
|
||||
{
|
||||
RemoveFromOutfit(new List<InventoryItem>(1) { item });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove specified items from the current outfit
|
||||
/// </summary>
|
||||
/// <param name="items">List of items to remove</param>
|
||||
public void RemoveFromOutfit(List<InventoryItem> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force rebaking textures
|
||||
/// </summary>
|
||||
public void RebakeTextures()
|
||||
{
|
||||
GetClient().Appearance.RequestSetAppearance(true);
|
||||
}
|
||||
|
||||
#endregion Public methods
|
||||
}
|
||||
}
|
||||
|
|
@ -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]");
|
||||
|
|
|
|||
1092
SecondBotEvents/Services/RLVService.cs
Normal file
1092
SecondBotEvents/Services/RLVService.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue