* Fix mantis 345 - it is now possible to duplicate prims directly in the region again without breakage
* This includes their inventories * Also, this revision properly synchronizes prim inventory crud.ThreadPoolClientBranch
parent
c3061717d0
commit
b33da2538e
|
@ -36,7 +36,13 @@ using System;
|
||||||
|
|
||||||
namespace OpenSim.Framework
|
namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
public class TaskInventoryDictionary : Dictionary<LLUUID, TaskInventoryItem>, IXmlSerializable
|
/// <summary>
|
||||||
|
/// A dictionary for task inventory.
|
||||||
|
///
|
||||||
|
/// This class is not thread safe. Callers must synchronize on Dictionary methods.
|
||||||
|
/// </summary>
|
||||||
|
public class TaskInventoryDictionary : Dictionary<LLUUID, TaskInventoryItem>,
|
||||||
|
ICloneable, IXmlSerializable
|
||||||
{
|
{
|
||||||
private static XmlSerializer tiiSerializer = new XmlSerializer(typeof(TaskInventoryItem));
|
private static XmlSerializer tiiSerializer = new XmlSerializer(typeof(TaskInventoryItem));
|
||||||
|
|
||||||
|
@ -86,19 +92,38 @@ namespace OpenSim.Framework
|
||||||
// see IXmlSerializable
|
// see IXmlSerializable
|
||||||
public void WriteXml(XmlWriter writer)
|
public void WriteXml(XmlWriter writer)
|
||||||
{
|
{
|
||||||
foreach (TaskInventoryItem item in Values)
|
lock (this)
|
||||||
{
|
{
|
||||||
tiiSerializer.Serialize(writer, item);
|
foreach (TaskInventoryItem item in Values)
|
||||||
|
{
|
||||||
|
tiiSerializer.Serialize(writer, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//tiiSerializer.Serialize(writer, Values);
|
//tiiSerializer.Serialize(writer, Values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see ICloneable
|
||||||
|
public Object Clone()
|
||||||
|
{
|
||||||
|
TaskInventoryDictionary clone = new TaskInventoryDictionary();
|
||||||
|
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
foreach (LLUUID uuid in Keys)
|
||||||
|
{
|
||||||
|
clone.Add(uuid, (TaskInventoryItem)this[uuid].Clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an item in a task inventory
|
/// Represents an item in a task inventory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TaskInventoryItem
|
public class TaskInventoryItem : ICloneable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// XXX This should really be factored out into some constants class.
|
/// XXX This should really be factored out into some constants class.
|
||||||
|
@ -141,17 +166,7 @@ namespace OpenSim.Framework
|
||||||
String.Empty,
|
String.Empty,
|
||||||
"lsltext",
|
"lsltext",
|
||||||
String.Empty
|
String.Empty
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reset the LLUUIDs for this item.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="partID">The new part ID to which this item belongs</param>
|
|
||||||
public void ResetIDs(LLUUID partID)
|
|
||||||
{
|
|
||||||
ItemID = LLUUID.Random();
|
|
||||||
ParentPartID = partID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LLUUID ItemID = LLUUID.Zero;
|
public LLUUID ItemID = LLUUID.Zero;
|
||||||
public LLUUID ParentID = LLUUID.Zero; //parent folder id
|
public LLUUID ParentID = LLUUID.Zero; //parent folder id
|
||||||
|
@ -175,5 +190,21 @@ namespace OpenSim.Framework
|
||||||
public uint CreationDate = 0;
|
public uint CreationDate = 0;
|
||||||
|
|
||||||
public LLUUID ParentPartID = LLUUID.Zero;
|
public LLUUID ParentPartID = LLUUID.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reset the LLUUIDs for this item.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="partID">The new part ID to which this item belongs</param>
|
||||||
|
public void ResetIDs(LLUUID partID)
|
||||||
|
{
|
||||||
|
ItemID = LLUUID.Random();
|
||||||
|
ParentPartID = partID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See ICloneable
|
||||||
|
public Object Clone()
|
||||||
|
{
|
||||||
|
return MemberwiseClone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -928,17 +928,22 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
{
|
{
|
||||||
SceneObjectGroup copy = originPrim.Copy(AgentID, GroupID);
|
SceneObjectGroup copy = originPrim.Copy(AgentID, GroupID);
|
||||||
copy.AbsolutePosition = copy.AbsolutePosition + offset;
|
copy.AbsolutePosition = copy.AbsolutePosition + offset;
|
||||||
|
copy.ResetIDs();
|
||||||
|
|
||||||
lock (Entities)
|
lock (Entities)
|
||||||
{
|
{
|
||||||
Entities.Add(copy.UUID, copy);
|
Entities.Add(copy.UUID, copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_numPrim++;
|
m_numPrim++;
|
||||||
|
|
||||||
|
copy.StartScripts();
|
||||||
copy.ScheduleGroupForFullUpdate();
|
copy.ScheduleGroupForFullUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MainLog.Instance.Warn("client", "Attempted to duplicate nonexistant prim");
|
MainLog.Instance.Warn("SCENE", "Attempted to duplicate nonexistant prim id {0}", GroupID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -572,9 +572,8 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// <param name="part"></param>
|
/// <param name="part"></param>
|
||||||
public void CopyRootPart(SceneObjectPart part, LLUUID cAgentID, LLUUID cGroupID)
|
public void CopyRootPart(SceneObjectPart part, LLUUID cAgentID, LLUUID cGroupID)
|
||||||
{
|
{
|
||||||
SceneObjectPart newPart = part.Copy(m_scene.PrimIDAllocate(), OwnerID, GroupID);
|
SceneObjectPart newPart = part.Copy(m_scene.PrimIDAllocate(), OwnerID, GroupID, m_parts.Count);
|
||||||
newPart.SetParent(this);
|
newPart.SetParent(this);
|
||||||
newPart.LinkNum = m_parts.Count;
|
|
||||||
m_parts.Add(newPart.UUID, newPart);
|
m_parts.Add(newPart.UUID, newPart);
|
||||||
SetPartAsRoot(newPart);
|
SetPartAsRoot(newPart);
|
||||||
}
|
}
|
||||||
|
@ -602,9 +601,8 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// <param name="part"></param>
|
/// <param name="part"></param>
|
||||||
public void CopyPart(SceneObjectPart part, LLUUID cAgentID, LLUUID cGroupID)
|
public void CopyPart(SceneObjectPart part, LLUUID cAgentID, LLUUID cGroupID)
|
||||||
{
|
{
|
||||||
SceneObjectPart newPart = part.Copy(m_scene.PrimIDAllocate(), OwnerID, GroupID);
|
SceneObjectPart newPart = part.Copy(m_scene.PrimIDAllocate(), OwnerID, GroupID, m_parts.Count);
|
||||||
newPart.SetParent(this);
|
newPart.SetParent(this);
|
||||||
newPart.LinkNum = m_parts.Count;
|
|
||||||
m_parts.Add(newPart.UUID, newPart);
|
m_parts.Add(newPart.UUID, newPart);
|
||||||
SetPartAsNonRoot(newPart);
|
SetPartAsNonRoot(newPart);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,13 +88,16 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// <param name="linkNum'>Link number for the part</param>
|
/// <param name="linkNum'>Link number for the part</param>
|
||||||
public void ResetInventoryIDs()
|
public void ResetInventoryIDs()
|
||||||
{
|
{
|
||||||
IList<TaskInventoryItem> items = new List<TaskInventoryItem>(TaskInventory.Values);
|
lock (TaskInventory)
|
||||||
TaskInventory.Clear();
|
{
|
||||||
|
IList<TaskInventoryItem> items = new List<TaskInventoryItem>(TaskInventory.Values);
|
||||||
foreach (TaskInventoryItem item in items)
|
TaskInventory.Clear();
|
||||||
{
|
|
||||||
item.ResetIDs(UUID);
|
foreach (TaskInventoryItem item in items)
|
||||||
TaskInventory.Add(item.ItemID, item);
|
{
|
||||||
|
item.ResetIDs(UUID);
|
||||||
|
TaskInventory.Add(item.ItemID, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,12 +106,15 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void StartScripts()
|
public void StartScripts()
|
||||||
{
|
{
|
||||||
foreach (TaskInventoryItem item in m_taskInventory.Values)
|
lock (m_taskInventory)
|
||||||
{
|
{
|
||||||
// XXX more hardcoding badness. Should be an enum in TaskInventoryItem
|
foreach (TaskInventoryItem item in m_taskInventory.Values)
|
||||||
if (10 == item.Type)
|
|
||||||
{
|
{
|
||||||
StartScript(item);
|
// XXX more hardcoding badness. Should be an enum in TaskInventoryItem
|
||||||
|
if (10 == item.Type)
|
||||||
|
{
|
||||||
|
StartScript(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,18 +155,20 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// </param>
|
/// </param>
|
||||||
public void StartScript(LLUUID itemId)
|
public void StartScript(LLUUID itemId)
|
||||||
{
|
{
|
||||||
if (m_taskInventory.ContainsKey(itemId))
|
lock (m_taskInventory)
|
||||||
{
|
{
|
||||||
StartScript(m_taskInventory[itemId]);
|
if (m_taskInventory.ContainsKey(itemId))
|
||||||
}
|
{
|
||||||
else
|
StartScript(m_taskInventory[itemId]);
|
||||||
{
|
}
|
||||||
MainLog.Instance.Error(
|
else
|
||||||
"PRIMINVENTORY",
|
{
|
||||||
"Couldn't start script with ID {0} since it couldn't be found for prim {1}, {2}",
|
MainLog.Instance.Error(
|
||||||
itemId, Name, UUID);
|
"PRIMINVENTORY",
|
||||||
}
|
"Couldn't start script with ID {0} since it couldn't be found for prim {1}, {2}",
|
||||||
|
itemId, Name, UUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -191,7 +199,12 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
item.ParentID = m_folderID;
|
item.ParentID = m_folderID;
|
||||||
item.CreationDate = 1000;
|
item.CreationDate = 1000;
|
||||||
item.ParentPartID = UUID;
|
item.ParentPartID = UUID;
|
||||||
m_taskInventory.Add(item.ItemID, item);
|
|
||||||
|
lock (m_taskInventory)
|
||||||
|
{
|
||||||
|
m_taskInventory.Add(item.ItemID, item);
|
||||||
|
}
|
||||||
|
|
||||||
m_inventorySerial++;
|
m_inventorySerial++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,9 +215,12 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// <param name="items"></param>
|
/// <param name="items"></param>
|
||||||
public void AddInventoryItems(ICollection<TaskInventoryItem> items)
|
public void AddInventoryItems(ICollection<TaskInventoryItem> items)
|
||||||
{
|
{
|
||||||
foreach (TaskInventoryItem item in items)
|
lock (m_taskInventory)
|
||||||
{
|
{
|
||||||
m_taskInventory.Add(item.ItemID, item);
|
foreach (TaskInventoryItem item in items)
|
||||||
|
{
|
||||||
|
m_taskInventory.Add(item.ItemID, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_inventorySerial++;
|
m_inventorySerial++;
|
||||||
|
@ -217,16 +233,19 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// <returns>null if the item does not exist</returns>
|
/// <returns>null if the item does not exist</returns>
|
||||||
public TaskInventoryItem GetInventoryItem(LLUUID itemID)
|
public TaskInventoryItem GetInventoryItem(LLUUID itemID)
|
||||||
{
|
{
|
||||||
if (m_taskInventory.ContainsKey(itemID))
|
lock (m_taskInventory)
|
||||||
{
|
{
|
||||||
return m_taskInventory[itemID];
|
if (m_taskInventory.ContainsKey(itemID))
|
||||||
}
|
{
|
||||||
else
|
return m_taskInventory[itemID];
|
||||||
{
|
}
|
||||||
MainLog.Instance.Error(
|
else
|
||||||
"PRIMINVENTORY",
|
{
|
||||||
"Tried to retrieve item ID {0} from prim {1}, {2} but the item does not exist in this inventory",
|
MainLog.Instance.Error(
|
||||||
itemID, Name, UUID);
|
"PRIMINVENTORY",
|
||||||
|
"Tried to retrieve item ID {0} from prim {1}, {2} but the item does not exist in this inventory",
|
||||||
|
itemID, Name, UUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -240,20 +259,23 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// <returns>false if the item did not exist, true if the update occurred succesfully</returns>
|
/// <returns>false if the item did not exist, true if the update occurred succesfully</returns>
|
||||||
public bool UpdateInventoryItem(TaskInventoryItem item)
|
public bool UpdateInventoryItem(TaskInventoryItem item)
|
||||||
{
|
{
|
||||||
if (m_taskInventory.ContainsKey(item.ItemID))
|
lock (m_taskInventory)
|
||||||
{
|
{
|
||||||
m_taskInventory[item.ItemID] = item;
|
if (m_taskInventory.ContainsKey(item.ItemID))
|
||||||
m_inventorySerial++;
|
{
|
||||||
|
m_taskInventory[item.ItemID] = item;
|
||||||
return true;
|
m_inventorySerial++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MainLog.Instance.Error(
|
||||||
|
"PRIMINVENTORY",
|
||||||
|
"Tried to retrieve item ID {0} from prim {1}, {2} but the item does not exist in this inventory",
|
||||||
|
item.ItemID, Name, UUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
MainLog.Instance.Error(
|
|
||||||
"PRIMINVENTORY",
|
|
||||||
"Tried to retrieve item ID {0} from prim {1}, {2} but the item does not exist in this inventory",
|
|
||||||
item.ItemID, Name, UUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -266,21 +288,24 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
/// in this prim's inventory.</returns>
|
/// in this prim's inventory.</returns>
|
||||||
public int RemoveInventoryItem(LLUUID itemID)
|
public int RemoveInventoryItem(LLUUID itemID)
|
||||||
{
|
{
|
||||||
if (m_taskInventory.ContainsKey(itemID))
|
lock (m_taskInventory)
|
||||||
{
|
{
|
||||||
int type = m_taskInventory[itemID].InvType;
|
if (m_taskInventory.ContainsKey(itemID))
|
||||||
m_taskInventory.Remove(itemID);
|
{
|
||||||
m_inventorySerial++;
|
int type = m_taskInventory[itemID].InvType;
|
||||||
|
m_taskInventory.Remove(itemID);
|
||||||
return type;
|
m_inventorySerial++;
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MainLog.Instance.Error(
|
||||||
|
"PRIMINVENTORY",
|
||||||
|
"Tried to remove item ID {0} from prim {1}, {2} but the item does not exist in this inventory",
|
||||||
|
itemID, Name, UUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
MainLog.Instance.Error(
|
|
||||||
"PRIMINVENTORY",
|
|
||||||
"Tried to remove item ID {0} from prim {1}, {2} but the item does not exist in this inventory",
|
|
||||||
itemID, Name, UUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -309,32 +334,36 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
{
|
{
|
||||||
byte[] fileData = new byte[0];
|
byte[] fileData = new byte[0];
|
||||||
InventoryStringBuilder invString = new InventoryStringBuilder(m_folderID, UUID);
|
InventoryStringBuilder invString = new InventoryStringBuilder(m_folderID, UUID);
|
||||||
foreach (TaskInventoryItem item in m_taskInventory.Values)
|
|
||||||
{
|
lock (m_taskInventory)
|
||||||
invString.AddItemStart();
|
{
|
||||||
invString.AddNameValueLine("item_id", item.ItemID.ToString());
|
foreach (TaskInventoryItem item in m_taskInventory.Values)
|
||||||
invString.AddNameValueLine("parent_id", item.ParentID.ToString());
|
{
|
||||||
|
invString.AddItemStart();
|
||||||
invString.AddPermissionsStart();
|
invString.AddNameValueLine("item_id", item.ItemID.ToString());
|
||||||
invString.AddNameValueLine("base_mask", "0x7FFFFFFF");
|
invString.AddNameValueLine("parent_id", item.ParentID.ToString());
|
||||||
invString.AddNameValueLine("owner_mask", "0x7FFFFFFF");
|
|
||||||
invString.AddNameValueLine("group_mask", "0x7FFFFFFF");
|
invString.AddPermissionsStart();
|
||||||
invString.AddNameValueLine("everyone_mask", "0x7FFFFFFF");
|
invString.AddNameValueLine("base_mask", "0x7FFFFFFF");
|
||||||
invString.AddNameValueLine("next_owner_mask", "0x7FFFFFFF");
|
invString.AddNameValueLine("owner_mask", "0x7FFFFFFF");
|
||||||
invString.AddNameValueLine("creator_id", item.CreatorID.ToString());
|
invString.AddNameValueLine("group_mask", "0x7FFFFFFF");
|
||||||
invString.AddNameValueLine("owner_id", item.OwnerID.ToString());
|
invString.AddNameValueLine("everyone_mask", "0x7FFFFFFF");
|
||||||
invString.AddNameValueLine("last_owner_id", item.LastOwnerID.ToString());
|
invString.AddNameValueLine("next_owner_mask", "0x7FFFFFFF");
|
||||||
invString.AddNameValueLine("group_id", item.GroupID.ToString());
|
invString.AddNameValueLine("creator_id", item.CreatorID.ToString());
|
||||||
invString.AddSectionEnd();
|
invString.AddNameValueLine("owner_id", item.OwnerID.ToString());
|
||||||
|
invString.AddNameValueLine("last_owner_id", item.LastOwnerID.ToString());
|
||||||
invString.AddNameValueLine("asset_id", item.AssetID.ToString());
|
invString.AddNameValueLine("group_id", item.GroupID.ToString());
|
||||||
invString.AddNameValueLine("type", TaskInventoryItem.Types[item.Type]);
|
invString.AddSectionEnd();
|
||||||
invString.AddNameValueLine("inv_type", TaskInventoryItem.InvTypes[item.InvType]);
|
|
||||||
invString.AddNameValueLine("flags", "0x00");
|
invString.AddNameValueLine("asset_id", item.AssetID.ToString());
|
||||||
invString.AddNameValueLine("name", item.Name + "|");
|
invString.AddNameValueLine("type", TaskInventoryItem.Types[item.Type]);
|
||||||
invString.AddNameValueLine("desc", item.Description + "|");
|
invString.AddNameValueLine("inv_type", TaskInventoryItem.InvTypes[item.InvType]);
|
||||||
invString.AddNameValueLine("creation_date", item.CreationDate.ToString());
|
invString.AddNameValueLine("flags", "0x00");
|
||||||
invString.AddSectionEnd();
|
invString.AddNameValueLine("name", item.Name + "|");
|
||||||
|
invString.AddNameValueLine("desc", item.Description + "|");
|
||||||
|
invString.AddNameValueLine("creation_date", item.CreationDate.ToString());
|
||||||
|
invString.AddSectionEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileData = Helpers.StringToField(invString.BuildString);
|
fileData = Helpers.StringToField(invString.BuildString);
|
||||||
|
|
|
@ -952,10 +952,10 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
#region Copying
|
#region Copying
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Duplicates this part.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public SceneObjectPart Copy(uint localID, LLUUID AgentID, LLUUID GroupID)
|
public SceneObjectPart Copy(uint localID, LLUUID AgentID, LLUUID GroupID, int linkNum)
|
||||||
{
|
{
|
||||||
SceneObjectPart dupe = (SceneObjectPart) MemberwiseClone();
|
SceneObjectPart dupe = (SceneObjectPart) MemberwiseClone();
|
||||||
dupe.m_shape = m_shape.Copy();
|
dupe.m_shape = m_shape.Copy();
|
||||||
|
@ -977,6 +977,10 @@ namespace OpenSim.Region.Environment.Scenes
|
||||||
dupe.ObjectSaleType = ObjectSaleType;
|
dupe.ObjectSaleType = ObjectSaleType;
|
||||||
dupe.SalePrice = SalePrice;
|
dupe.SalePrice = SalePrice;
|
||||||
dupe.Category = Category;
|
dupe.Category = Category;
|
||||||
|
|
||||||
|
dupe.TaskInventory = (TaskInventoryDictionary)dupe.TaskInventory.Clone();
|
||||||
|
|
||||||
|
dupe.ResetIDs(linkNum);
|
||||||
|
|
||||||
// This may be wrong... it might have to be applied in SceneObjectGroup to the object that's being duplicated.
|
// This may be wrong... it might have to be applied in SceneObjectGroup to the object that's being duplicated.
|
||||||
dupe.LastOwnerID = ObjectOwner;
|
dupe.LastOwnerID = ObjectOwner;
|
||||||
|
|
Loading…
Reference in New Issue