Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions LibreMetaverse/Caps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public string CapabilityNameFromURI(Uri cap)
"ChatSessionRequest",
"CopyInventoryFromNotecard",
"CreateInventoryCategory",
"CreateTaskInventoryItem",
"DeclineFriendship",
"DeclineGroupInvite",
"DispatchRegionInfo",
Expand Down
10 changes: 10 additions & 0 deletions LibreMetaverse/Inventory/InventoryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ public override string ToString()
/// <summary>Combined flags from <see cref="InventoryItemFlags"/></summary>
[Key("Flags")]
public uint Flags { get; set; }
/// <summary>VM runtime for scripts (e.g. "lsl2", "mono", "luau"), from the task inventory response's metadata; null if not a script or not provided</summary>
[Key("Runtime")]
public string? Runtime { get; set; }

/// <summary>Time and date the inventory item was created, stored as
/// UTC (Coordinated Universal Time)</summary>
Expand Down Expand Up @@ -269,6 +272,9 @@ public static InventoryItem FromOSD(OSD data)
"inv_type": "script",
"item_id": "806baac6-64cd-daae-efff-627208ed1d2b",
"metadata": {
"script": {
"runtime": "luau"
}
},
"name": "New Script",
"parent_id": "74a61033-366a-0b89-4d4b-649c5b0de2ad",
Expand Down Expand Up @@ -324,6 +330,10 @@ public static InventoryItem FromOSD(OSD data)
item.AssetType = assetType;
item.CreationDate = Utils.UnixTimeToDateTime(descItem["created_at"]);
item.Flags = descItem["flags"];
if (descItem["metadata"] is OSDMap meta && meta["script"] is OSDMap scriptMeta && scriptMeta.TryGetValue("runtime", out var runtime))
{
item.Runtime = runtime.AsString();
}

OSDMap perms = (OSDMap)descItem["permissions"];
item.OwnerID = perms["owner_id"];
Expand Down
74 changes: 70 additions & 4 deletions LibreMetaverse/Inventory/InventoryManager.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,13 @@ private void SendCopyFromNotecardPacket(UUID objectID, UUID notecardID, UUID fol
}

public async Task<(bool uploadSuccess, string uploadStatus, bool compileSuccess, List<string>? compileMessages, UUID itemID, UUID assetID)> RequestUpdateScriptAgentInventoryAsync(
byte[] data, UUID itemID, bool mono, CancellationToken cancellationToken = default, IProgress<ProgressReport>? progress = null)
byte[] data, UUID itemID, ScriptTarget target = ScriptTarget.Mono, CancellationToken cancellationToken = default, IProgress<ProgressReport>? progress = null)
Comment on lines 364 to +365
{
var cap = GetCapabilityURI("UpdateScriptAgent");
if (cap == null)
throw new InvalidOperationException("UpdateScriptAgent capability is not currently available");

var request = new UpdateScriptAgentRequestMessage { ItemID = itemID, Target = mono ? "mono" : "lsl2" };
var request = new UpdateScriptAgentRequestMessage { ItemID = itemID, Target = ScriptTargetToString(target) };
try
{
var result = await PostCapAsync(cap, request.Serialize(), cancellationToken, progress).ConfigureAwait(false);
Expand All @@ -379,13 +379,13 @@ private void SendCopyFromNotecardPacket(UUID objectID, UUID notecardID, UUID fol
}

public async Task<(bool uploadSuccess, string uploadStatus, bool compileSuccess, List<string>? compileMessages, UUID itemID, UUID assetID)> RequestUpdateScriptTaskAsync(
byte[] data, UUID itemID, UUID taskID, bool mono, bool running, CancellationToken cancellationToken = default, IProgress<ProgressReport>? progress = null)
byte[] data, UUID itemID, UUID taskID, ScriptTarget target, bool running, CancellationToken cancellationToken = default, IProgress<ProgressReport>? progress = null)
{
var cap = GetCapabilityURI("UpdateScriptTask");
if (cap == null)
throw new InvalidOperationException("UpdateScriptTask capability is not currently available");

var msg = new UpdateScriptTaskUpdateMessage { ItemID = itemID, TaskID = taskID, ScriptRunning = running, Target = mono ? "mono" : "lsl2" };
var msg = new UpdateScriptTaskUpdateMessage { ItemID = itemID, TaskID = taskID, ScriptRunning = running, Target = ScriptTargetToString(target) };
try
{
var result = await PostCapAsync(cap, msg.Serialize(), cancellationToken, progress).ConfigureAwait(false);
Expand All @@ -395,6 +395,49 @@ private void SendCopyFromNotecardPacket(UUID objectID, UUID notecardID, UUID fol
catch (Exception ex) { return (false, ex.Message, false, null, UUID.Zero, UUID.Zero); }
}

public async Task<(bool success, string name)> RequestCreateTaskScriptAsync(
UUID objectID, string name, string description, string vm, ScriptLanguage language,
CancellationToken cancellationToken = default)
{
var cap = GetCapabilityURI("CreateTaskInventoryItem");
if (cap == null)
throw new InvalidOperationException("CreateTaskInventoryItem capability is not currently available");

var msg = new CreateTaskInventoryItemMessage
{
ObjectID = objectID,
AssetType = AssetType.LSLText,
SubType = (int)language,
Name = name,
Description = description,
Vm = vm,
Enabled = true,
Comment thread
mercurylinden marked this conversation as resolved.
Outdated
Permissions = new Permissions(
(uint)int.MaxValue,
0,
0,
(uint)(PermissionMask.Transfer | PermissionMask.Move),
(uint)int.MaxValue)
};

try
{
var result = await PostCapAsync(cap, msg.Serialize(), cancellationToken).ConfigureAwait(false);
if (result is OSDMap map)
{
var success = map["success"].AsBoolean();
return (success, success ? map["name"].AsString() : map["message"].AsString());
}
return (false, string.Empty);
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
{
Logger.Error($"RequestCreateTaskScriptAsync failed for object {objectID}", ex, Client);
return (false, ex.Message);
}
}

/// <summary>
/// Async-first variant to request a single inventory item. Uses FetchInventory2 capability when available.
/// </summary>
Expand Down Expand Up @@ -1460,6 +1503,29 @@ public async Task<CopyItemsResult> RequestCopyItemsWithResultAsync(List<UUID> it
return new CopyItemsResult { Success = false, CopiedItems = null, Error = ex };
}
}

public enum ScriptLanguage
{
LSL = 0,
Lua = 1
}

public enum ScriptTarget
{
LSL2,
Mono,
Luau,
LSLLuau
}

private static string ScriptTargetToString(ScriptTarget target) => target switch
{
ScriptTarget.LSL2 => "lsl2",
ScriptTarget.Mono => "mono",
ScriptTarget.Luau => "luau",
ScriptTarget.LSLLuau => "lsl-luau",
_ => "mono"
};
Comment thread
mercurylinden marked this conversation as resolved.
}
}

5 changes: 5 additions & 0 deletions LibreMetaverse/Inventory/InventoryManager.Handlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ private async Task<OSD> PostCapAsync(Uri uri, OSD payload, CancellationToken can
var result = await Client.HttpCapsClient.PostAsync(uri, OSDFormat.Xml, payload, cancellationToken, progress).ConfigureAwait(false);
var responseData = result.data ?? throw new InvalidOperationException("Empty response from capability POST");

if (result.response.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new System.Net.Http.HttpRequestException($"{result.response.StatusCode}({(int)result.response.StatusCode}): {System.Text.Encoding.UTF8.GetString(responseData)}");
}
Comment on lines +47 to +50

try { return OSDParser.Deserialize(responseData); }
catch (Exception ex) { throw new InvalidOperationException($"Failed to parse capability response: {ex.Message}", ex); }
}
Expand Down
60 changes: 60 additions & 0 deletions LibreMetaverse/Messages/LindenMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2736,6 +2736,66 @@ public void Deserialize(OSDMap map)
}
}

/// <summary>Request POSTed to the CreateTaskInventoryItem capability to create a new item in a task's inventory.</summary>
public class CreateTaskInventoryItemMessage : IMessage
{
/// <summary>UUID of the in-world object whose inventory will receive the new item</summary>
public UUID ObjectID;
public AssetType AssetType;
/// <summary>Script sub-type: 0 = LSL, 1 = Lua (SST_LUA); ignored for non-script types</summary>
public int SubType;
public string Name;
public string Description;
public Permissions Permissions;
/// <summary>VM runtime for scripts: "lsl2", "mono", or "luau" (required for Lua); null = omit (use for notecards)</summary>
public string? Vm;
/// <summary> Whether the script starts enabled (default: true)</summary>
public bool? Enabled;
/// <summary>Optional template asset; UUID.Zero = server default</summary>
public UUID TemplateID;
/// <summary>Optional initial text content for notecards; null/empty = omitted</summary>
public string? Text;

public OSDMap Serialize()
{
var paramsMap = new OSDMap();
if (Vm != null)
{
paramsMap["vm"] = OSD.FromString(Vm);
}
if (Enabled != null)
{
bool not_null_enabled = (bool)Enabled;
paramsMap["enabled"] = OSD.FromBoolean(not_null_enabled);
}
Comment thread
mercurylinden marked this conversation as resolved.
Outdated
if (TemplateID != UUID.Zero)
paramsMap["template_id"] = OSD.FromUUID(TemplateID);
if (!string.IsNullOrEmpty(Text))
paramsMap["text"] = OSD.FromString(Text);

return new OSDMap
{
["object_id"] = OSD.FromUUID(ObjectID),
["asset_type"] = OSD.FromInteger((int)AssetType),
["sub_type"] = OSD.FromInteger(SubType),
["name"] = OSD.FromString(Name),
["description"] = OSD.FromString(Description),
["permissions"] = new OSDMap
{
["base"] = OSD.FromInteger((int)Permissions.BaseMask),
["owner"] = OSD.FromInteger((int)Permissions.OwnerMask),
["everyone"] = OSD.FromInteger((int)Permissions.EveryoneMask),
["group"] = OSD.FromInteger((int)Permissions.GroupMask),
["next_owner"] = OSD.FromInteger((int)Permissions.NextOwnerMask)
},
["params"] = paramsMap
};
}

// Request-only message; Deserialize is not used
public void Deserialize(OSDMap map) { }
Comment thread
mercurylinden marked this conversation as resolved.
Outdated
}


/// <summary>
/// Metadata POSTed to the SendPostcard capability. This is the first step of a two-phase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override async Task<string> ExecuteAsync(string[] args, UUID fromAgentId)

using var uploadCts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var (uploadSuccess, _, compileSuccess, _, itemId, assetId) = await Client.Inventory
.RequestUpdateScriptAgentInventoryAsync(Encoding.UTF8.GetBytes(body), createdItem.UUID, true, uploadCts.Token).ConfigureAwait(false);
.RequestUpdateScriptAgentInventoryAsync(Encoding.UTF8.GetBytes(body), createdItem.UUID, InventoryManager.ScriptTarget.Mono, uploadCts.Token).ConfigureAwait(false);

var log = $"Filename: {file}";
log += uploadSuccess ? $" Script successfully uploaded, ItemID {itemId} AssetID {assetId}" : $" Script failed to upload, ItemID {itemId}";
Expand Down