mirror of
https://github.com/TechplexEngineer/plexview.git
synced 2026-07-27 22:15:37 +00:00
Initial commit
I wish I could figure out how to have PlexVie be out of the LibOMV tree but its just not working die to a DLL not found exception about libopenjpeg. (Yes the dll is in the bin folder) This will do for now
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace PlexView
|
||||
{
|
||||
public class PlexView
|
||||
{
|
||||
public PlexView (string[] args)
|
||||
{
|
||||
GridClient client = new GridClient();
|
||||
client.Settings.LOGIN_SERVER = "http://login.osgrid.org/";
|
||||
if (client.Network.Login("", "", "", "FirstBot", "1.0"))
|
||||
{
|
||||
// Yay we made it! let's print out the message of the day
|
||||
Console.WriteLine("You have successfully logged into Second Life!\n The Message of the day is {0}\nPress any Key to Logout",
|
||||
client.Network.LoginMessage);
|
||||
|
||||
Console.ReadLine(); // Wait for user to press a key before we continue
|
||||
|
||||
client.Network.Logout(); // Lets logout since we're done here
|
||||
}
|
||||
else
|
||||
{
|
||||
// tell the user why the login failed
|
||||
Console.WriteLine("We were unable to login to Second Life, The Login Server said: {0}",
|
||||
client.Network.LoginMessage);
|
||||
}
|
||||
Console.WriteLine("Press Any Key to Exit");
|
||||
Console.ReadLine(); // Wait for user to press a key before we exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" ?>
|
||||
<project name="PlexView" default="build">
|
||||
<target name="build">
|
||||
<echo message="Build Directory is ${build.dir}" />
|
||||
<mkdir dir="${build.dir}" />
|
||||
<csc target="exe" debug="${build.debug}" unsafe="True" warnaserror="False" define="TRACE" nostdlib="False" main="" output="${build.dir}/${project::get-name()}.exe">
|
||||
<resources prefix="PlexView" dynamicprefix="true" >
|
||||
</resources>
|
||||
<sources failonempty="true">
|
||||
<include name="PlexView.cs" />
|
||||
<include name="Program.cs" />
|
||||
<include name="Properties/AssemblyInfo.cs" />
|
||||
</sources>
|
||||
<references basedir="${project::get-base-directory()}">
|
||||
<lib>
|
||||
<include name="${project::get-base-directory()}" />
|
||||
<include name="${build.dir}" />
|
||||
</lib>
|
||||
<include name="System.dll" />
|
||||
<include name="${build.dir}/OpenMetaverse.dll" />
|
||||
<include name="${build.dir}/OpenMetaverseTypes.dll" />
|
||||
<include name="${build.dir}/OpenMetaverse.Utilities.dll" />
|
||||
<include name="${build.dir}/OpenMetaverse.StructuredData.dll" />
|
||||
</references>
|
||||
<nowarn>
|
||||
<warning number="1591" />
|
||||
<warning number="1574" />
|
||||
<warning number="0419" />
|
||||
<warning number="0618" />
|
||||
</nowarn>
|
||||
</csc>
|
||||
</target>
|
||||
<target name="clean">
|
||||
<delete dir="${bin.dir}" failonerror="false" />
|
||||
<delete dir="${obj.dir}" failonerror="false" />
|
||||
</target>
|
||||
<target name="doc" description="Creates documentation.">
|
||||
</target>
|
||||
</project>
|
||||
@@ -0,0 +1,31 @@
|
||||
<Project name="PlexView" description="" standardNamespace="PlexView" newfilesearch="None" enableviewstate="True" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject">
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release" ctype="DotNetProjectConfiguration">
|
||||
<Output directory="./../../../bin/" assembly="PlexView" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
<Build debugmode="True" target="Exe" />
|
||||
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
|
||||
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="False" optimize="True" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Exe" definesymbols="TRACE" generatexmldocumentation="False" win32Icon="" ctype="CSharpCompilerParameters" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug" ctype="DotNetProjectConfiguration">
|
||||
<Output directory="./../../../bin/" assembly="PlexView" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
<Build debugmode="True" target="Exe" />
|
||||
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
|
||||
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="True" optimize="False" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Exe" definesymbols="TRACE;DEBUG" generatexmldocumentation="False" win32Icon="" ctype="CSharpCompilerParameters" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<DeploymentInformation target="" script="" strategy="File">
|
||||
<excludeFiles />
|
||||
</DeploymentInformation>
|
||||
<Contents>
|
||||
<File name="./PlexView.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Program.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Properties/AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
</Contents>
|
||||
<References>
|
||||
<ProjectReference type="Gac" localcopy="False" refto="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverse" />
|
||||
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverseTypes" />
|
||||
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverse.Utilities" />
|
||||
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverse.StructuredData" />
|
||||
</References>
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace PlexView
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
new PlexView(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("PlexView")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("PlexView")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("83715687-9058-4d2a-8090-d8726dd5cd74")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
|
||||
</configSections>
|
||||
|
||||
<log4net>
|
||||
<appender name="FileAppender" type="log4net.Appender.FileAppender">
|
||||
<file value="PlexView.log.txt" />
|
||||
<appendToFile value="true" />
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value="%date [%thread] %-5level - %message%newline" />
|
||||
</layout>
|
||||
</appender>
|
||||
<logger name="OpenMetaverse">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="FileAppender" />
|
||||
</logger>
|
||||
</log4net>
|
||||
</configuration>
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace CommandLine.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Arguments class
|
||||
/// </summary>
|
||||
public class Arguments
|
||||
{
|
||||
// Variables
|
||||
private StringDictionary Parameters;
|
||||
|
||||
// Constructor
|
||||
public Arguments(string[] Args)
|
||||
{
|
||||
Parameters = new StringDictionary();
|
||||
Regex Splitter = new Regex(@"^-{1,2}|=",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
Regex Remover = new Regex(@"^['""]?(.*?)['""]?$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
string Parameter = null;
|
||||
string[] Parts;
|
||||
|
||||
// Valid parameters forms:
|
||||
// {-,/,--}param{ ,=,:}((",')value(",'))
|
||||
// Examples:
|
||||
// -param1 value1 --param2
|
||||
// /param4=happy -param5 '--=nice=--'
|
||||
foreach (string Txt in Args)
|
||||
{
|
||||
// Look for new parameters (-,/ or --) and a
|
||||
// possible enclosed value (=,:)
|
||||
Parts = Splitter.Split(Txt, 3);
|
||||
|
||||
switch (Parts.Length)
|
||||
{
|
||||
// Found a value (for the last parameter
|
||||
// found (space separator))
|
||||
case 1:
|
||||
if (Parameter != null)
|
||||
{
|
||||
if (!Parameters.ContainsKey(Parameter))
|
||||
{
|
||||
Parts[0] =
|
||||
Remover.Replace(Parts[0], "$1");
|
||||
|
||||
Parameters.Add(Parameter, Parts[0]);
|
||||
}
|
||||
Parameter = null;
|
||||
}
|
||||
// else Error: no parameter waiting for a value (skipped)
|
||||
break;
|
||||
|
||||
// Found just a parameter
|
||||
case 2:
|
||||
// The last parameter is still waiting.
|
||||
// With no value, set it to true.
|
||||
if (Parameter != null)
|
||||
{
|
||||
if (!Parameters.ContainsKey(Parameter))
|
||||
Parameters.Add(Parameter, "true");
|
||||
}
|
||||
Parameter = Parts[1];
|
||||
break;
|
||||
|
||||
// Parameter with enclosed value
|
||||
case 3:
|
||||
// The last parameter is still waiting.
|
||||
// With no value, set it to true.
|
||||
if (Parameter != null)
|
||||
{
|
||||
if (!Parameters.ContainsKey(Parameter))
|
||||
Parameters.Add(Parameter, "true");
|
||||
}
|
||||
|
||||
Parameter = Parts[1];
|
||||
|
||||
// Remove possible enclosing characters (",')
|
||||
if (!Parameters.ContainsKey(Parameter))
|
||||
{
|
||||
Parts[2] = Remover.Replace(Parts[2], "$1");
|
||||
Parameters.Add(Parameter, Parts[2]);
|
||||
}
|
||||
|
||||
Parameter = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// In case a parameter is still waiting
|
||||
if (Parameter != null)
|
||||
{
|
||||
if (!Parameters.ContainsKey(Parameter))
|
||||
Parameters.Add(Parameter, "true");
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve a parameter value if it exists
|
||||
// (overriding C# indexer property)
|
||||
public string this[string Param]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Parameters[Param]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class LoginDetails
|
||||
{
|
||||
public string FirstName;
|
||||
public string LastName;
|
||||
public string Password;
|
||||
public string StartLocation;
|
||||
public bool GroupCommands;
|
||||
public string MasterName;
|
||||
public UUID MasterKey;
|
||||
public string URI;
|
||||
}
|
||||
|
||||
public class StartPosition
|
||||
{
|
||||
public string sim;
|
||||
public int x;
|
||||
public int y;
|
||||
public int z;
|
||||
|
||||
public StartPosition()
|
||||
{
|
||||
this.sim = null;
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ClientManager
|
||||
{
|
||||
const string VERSION = "1.0.0";
|
||||
|
||||
class Singleton { internal static readonly ClientManager Instance = new ClientManager(); }
|
||||
public static ClientManager Instance { get { return Singleton.Instance; } }
|
||||
|
||||
public Dictionary<UUID, TestClient> Clients = new Dictionary<UUID, TestClient>();
|
||||
public Dictionary<Simulator, Dictionary<uint, Primitive>> SimPrims = new Dictionary<Simulator, Dictionary<uint, Primitive>>();
|
||||
|
||||
public bool Running = true;
|
||||
public bool GetTextures = false;
|
||||
public volatile int PendingLogins = 0;
|
||||
public string onlyAvatar = String.Empty;
|
||||
|
||||
ClientManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(List<LoginDetails> accounts, bool getTextures)
|
||||
{
|
||||
GetTextures = getTextures;
|
||||
|
||||
foreach (LoginDetails account in accounts)
|
||||
Login(account);
|
||||
}
|
||||
|
||||
public TestClient Login(string[] args)
|
||||
{
|
||||
if (args.Length < 3)
|
||||
{
|
||||
Console.WriteLine("Usage: login firstname lastname password [simname] [login server url]");
|
||||
return null;
|
||||
}
|
||||
LoginDetails account = new LoginDetails();
|
||||
account.FirstName = args[0];
|
||||
account.LastName = args[1];
|
||||
account.Password = args[2];
|
||||
|
||||
if (args.Length > 3)
|
||||
{
|
||||
// If it looks like a full starting position was specified, parse it
|
||||
if (args[3].StartsWith("http"))
|
||||
{
|
||||
account.URI = args[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args[3].IndexOf('/') >= 0)
|
||||
{
|
||||
char sep = '/';
|
||||
string[] startbits = args[3].Split(sep);
|
||||
try
|
||||
{
|
||||
account.StartLocation = NetworkManager.StartLocation(startbits[0], Int32.Parse(startbits[1]),
|
||||
Int32.Parse(startbits[2]), Int32.Parse(startbits[3]));
|
||||
}
|
||||
catch (FormatException) { }
|
||||
}
|
||||
|
||||
// Otherwise, use the center of the named region
|
||||
if (account.StartLocation == null)
|
||||
account.StartLocation = NetworkManager.StartLocation(args[3], 128, 128, 40);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Length > 4)
|
||||
if (args[4].StartsWith("http"))
|
||||
account.URI = args[4];
|
||||
|
||||
if (string.IsNullOrEmpty(account.URI))
|
||||
account.URI = Program.LoginURI;
|
||||
Logger.Log("Using login URI " + account.URI, Helpers.LogLevel.Info);
|
||||
|
||||
return Login(account);
|
||||
}
|
||||
|
||||
public TestClient Login(LoginDetails account)
|
||||
{
|
||||
// Check if this client is already logged in
|
||||
foreach (TestClient c in Clients.Values)
|
||||
{
|
||||
if (c.Self.FirstName == account.FirstName && c.Self.LastName == account.LastName)
|
||||
{
|
||||
Logout(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++PendingLogins;
|
||||
|
||||
TestClient client = new TestClient(this);
|
||||
client.Network.LoginProgress +=
|
||||
delegate(object sender, LoginProgressEventArgs e)
|
||||
{
|
||||
Logger.Log(String.Format("Login {0}: {1}", e.Status, e.Message), Helpers.LogLevel.Info, client);
|
||||
|
||||
if (e.Status == LoginStatus.Success)
|
||||
{
|
||||
Clients[client.Self.AgentID] = client;
|
||||
|
||||
if (client.MasterKey == UUID.Zero)
|
||||
{
|
||||
UUID query = UUID.Zero;
|
||||
EventHandler<DirPeopleReplyEventArgs> peopleDirCallback =
|
||||
delegate(object sender2, DirPeopleReplyEventArgs dpe)
|
||||
{
|
||||
if (dpe.QueryID == query)
|
||||
{
|
||||
if (dpe.MatchedPeople.Count != 1)
|
||||
{
|
||||
Logger.Log("Unable to resolve master key from " + client.MasterName, Helpers.LogLevel.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.MasterKey = dpe.MatchedPeople[0].AgentID;
|
||||
Logger.Log("Master key resolved to " + client.MasterKey, Helpers.LogLevel.Info);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
client.Directory.DirPeopleReply += peopleDirCallback;
|
||||
query = client.Directory.StartPeopleSearch(client.MasterName, 0);
|
||||
}
|
||||
|
||||
Logger.Log("Logged in " + client.ToString(), Helpers.LogLevel.Info);
|
||||
--PendingLogins;
|
||||
}
|
||||
else if (e.Status == LoginStatus.Failed)
|
||||
{
|
||||
Logger.Log("Failed to login " + account.FirstName + " " + account.LastName + ": " +
|
||||
client.Network.LoginMessage, Helpers.LogLevel.Warning);
|
||||
--PendingLogins;
|
||||
}
|
||||
};
|
||||
|
||||
// Optimize the throttle
|
||||
client.Throttle.Wind = 0;
|
||||
client.Throttle.Cloud = 0;
|
||||
client.Throttle.Land = 1000000;
|
||||
client.Throttle.Task = 1000000;
|
||||
|
||||
client.GroupCommands = account.GroupCommands;
|
||||
client.MasterName = account.MasterName;
|
||||
client.MasterKey = account.MasterKey;
|
||||
client.AllowObjectMaster = client.MasterKey != UUID.Zero; // Require UUID for object master.
|
||||
|
||||
LoginParams loginParams = client.Network.DefaultLoginParams(
|
||||
account.FirstName, account.LastName, account.Password, "TestClient", VERSION);
|
||||
|
||||
if (!String.IsNullOrEmpty(account.StartLocation))
|
||||
loginParams.Start = account.StartLocation;
|
||||
|
||||
if (!String.IsNullOrEmpty(account.URI))
|
||||
loginParams.URI = account.URI;
|
||||
|
||||
client.Network.BeginLogin(loginParams);
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public void Run(bool noGUI)
|
||||
{
|
||||
if (noGUI)
|
||||
{
|
||||
while (Running)
|
||||
{
|
||||
Thread.Sleep(2 * 1000);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Console.WriteLine("Type quit to exit. Type help for a command list.");
|
||||
|
||||
while (Running)
|
||||
{
|
||||
PrintPrompt();
|
||||
string input = Console.ReadLine();
|
||||
DoCommandAll(input, UUID.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GridClient client in Clients.Values)
|
||||
{
|
||||
if (client.Network.Connected)
|
||||
client.Network.Logout();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintPrompt()
|
||||
{
|
||||
int online = 0;
|
||||
|
||||
foreach (GridClient client in Clients.Values)
|
||||
{
|
||||
if (client.Network.Connected) online++;
|
||||
}
|
||||
|
||||
Console.Write(online + " avatars online> ");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="cmd"></param>
|
||||
/// <param name="fromAgentID"></param>
|
||||
/// <param name="imSessionID"></param>
|
||||
public void DoCommandAll(string cmd, UUID fromAgentID)
|
||||
{
|
||||
string[] tokens = cmd.Trim().Split(new char[] { ' ', '\t' });
|
||||
if (tokens.Length == 0)
|
||||
return;
|
||||
|
||||
string firstToken = tokens[0].ToLower();
|
||||
if (String.IsNullOrEmpty(firstToken))
|
||||
return;
|
||||
|
||||
// Allow for comments when cmdline begins with ';' or '#'
|
||||
if (firstToken[0] == ';' || firstToken[0] == '#')
|
||||
return;
|
||||
|
||||
if ('@' == firstToken[0]) {
|
||||
onlyAvatar = String.Empty;
|
||||
if (tokens.Length == 3) {
|
||||
bool found = false;
|
||||
onlyAvatar = tokens[1]+" "+tokens[2];
|
||||
foreach (TestClient client in Clients.Values) {
|
||||
if ((client.ToString() == onlyAvatar) && (client.Network.Connected)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
Logger.Log("Commanding only "+onlyAvatar+" now", Helpers.LogLevel.Info);
|
||||
} else {
|
||||
Logger.Log("Commanding nobody now. Avatar "+onlyAvatar+" is offline", Helpers.LogLevel.Info);
|
||||
}
|
||||
} else {
|
||||
Logger.Log("Commanding all avatars now", Helpers.LogLevel.Info);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
string[] args = new string[tokens.Length - 1];
|
||||
if (args.Length > 0)
|
||||
Array.Copy(tokens, 1, args, 0, args.Length);
|
||||
|
||||
if (firstToken == "login")
|
||||
{
|
||||
Login(args);
|
||||
}
|
||||
else if (firstToken == "quit")
|
||||
{
|
||||
Quit();
|
||||
Logger.Log("All clients logged out and program finished running.", Helpers.LogLevel.Info);
|
||||
}
|
||||
else if (firstToken == "help")
|
||||
{
|
||||
if (Clients.Count > 0)
|
||||
{
|
||||
foreach (TestClient client in Clients.Values)
|
||||
{
|
||||
Console.WriteLine(client.Commands["help"].Execute(args, UUID.Zero));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("You must login at least one bot to use the help command");
|
||||
}
|
||||
}
|
||||
else if (firstToken == "script")
|
||||
{
|
||||
// No reason to pass this to all bots, and we also want to allow it when there are no bots
|
||||
ScriptCommand command = new ScriptCommand(null);
|
||||
Logger.Log(command.Execute(args, UUID.Zero), Helpers.LogLevel.Info);
|
||||
}
|
||||
else if (firstToken == "waitforlogin")
|
||||
{
|
||||
// Special exception to allow this to run before any bots have logged in
|
||||
if (ClientManager.Instance.PendingLogins > 0)
|
||||
{
|
||||
WaitForLoginCommand command = new WaitForLoginCommand(null);
|
||||
Logger.Log(command.Execute(args, UUID.Zero), Helpers.LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("No pending logins", Helpers.LogLevel.Info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make an immutable copy of the Clients dictionary to safely iterate over
|
||||
Dictionary<UUID, TestClient> clientsCopy = new Dictionary<UUID, TestClient>(Clients);
|
||||
|
||||
int completed = 0;
|
||||
|
||||
foreach (TestClient client in clientsCopy.Values)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem((WaitCallback)
|
||||
delegate(object state)
|
||||
{
|
||||
TestClient testClient = (TestClient)state;
|
||||
if ((String.Empty == onlyAvatar) || (testClient.ToString() == onlyAvatar)) {
|
||||
if (testClient.Commands.ContainsKey(firstToken)) {
|
||||
string result;
|
||||
try {
|
||||
result = testClient.Commands[firstToken].Execute(args, fromAgentID);
|
||||
Logger.Log(result, Helpers.LogLevel.Info, testClient);
|
||||
} catch(Exception e) {
|
||||
Logger.Log(String.Format("{0} raised exception {1}", firstToken, e),
|
||||
Helpers.LogLevel.Error,
|
||||
testClient);
|
||||
}
|
||||
} else
|
||||
Logger.Log("Unknown command " + firstToken, Helpers.LogLevel.Warning);
|
||||
}
|
||||
|
||||
++completed;
|
||||
},
|
||||
client);
|
||||
}
|
||||
|
||||
while (completed < clientsCopy.Count)
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
public void Logout(TestClient client)
|
||||
{
|
||||
Clients.Remove(client.Self.AgentID);
|
||||
client.Network.Logout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public void Quit()
|
||||
{
|
||||
Running = false;
|
||||
// TODO: It would be really nice if we could figure out a way to abort the ReadLine here in so that Run() will exit.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public enum CommandCategory : int
|
||||
{
|
||||
Parcel,
|
||||
Appearance,
|
||||
Movement,
|
||||
Simulator,
|
||||
Communication,
|
||||
Inventory,
|
||||
Objects,
|
||||
Voice,
|
||||
TestClient,
|
||||
Friends,
|
||||
Groups,
|
||||
Other,
|
||||
Unknown,
|
||||
Search
|
||||
}
|
||||
|
||||
public abstract class Command : IComparable
|
||||
{
|
||||
public string Name;
|
||||
public string Description;
|
||||
public CommandCategory Category;
|
||||
|
||||
public TestClient Client;
|
||||
|
||||
public abstract string Execute(string[] args, UUID fromAgentID);
|
||||
|
||||
/// <summary>
|
||||
/// When set to true, think will be called.
|
||||
/// </summary>
|
||||
public bool Active;
|
||||
|
||||
/// <summary>
|
||||
/// Called twice per second, when Command.Active is set to true.
|
||||
/// </summary>
|
||||
public virtual void Think()
|
||||
{
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is Command)
|
||||
{
|
||||
Command c2 = (Command)obj;
|
||||
return Category.CompareTo(c2.Category);
|
||||
}
|
||||
else
|
||||
throw new ArgumentException("Object is not of type Command.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class BotsCommand : Command
|
||||
{
|
||||
private Dictionary<UUID, bool> m_AgentList = new Dictionary<UUID, bool>();
|
||||
|
||||
public BotsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "bots";
|
||||
Description = "detects avatars that appear to be bots.";
|
||||
Category = CommandCategory.Other;
|
||||
testClient.Avatars.ViewerEffect += new EventHandler<ViewerEffectEventArgs>(Avatars_ViewerEffect);
|
||||
testClient.Avatars.ViewerEffectLookAt += new EventHandler<ViewerEffectLookAtEventArgs>(Avatars_ViewerEffectLookAt);
|
||||
testClient.Avatars.ViewerEffectPointAt += new EventHandler<ViewerEffectPointAtEventArgs>(Avatars_ViewerEffectPointAt);
|
||||
}
|
||||
|
||||
private void Avatars_ViewerEffectPointAt(object sender, ViewerEffectPointAtEventArgs e)
|
||||
{
|
||||
lock (m_AgentList)
|
||||
{
|
||||
if (m_AgentList.ContainsKey(e.SourceID))
|
||||
m_AgentList[e.SourceID] = true;
|
||||
else
|
||||
m_AgentList.Add(e.SourceID, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void Avatars_ViewerEffectLookAt(object sender, ViewerEffectLookAtEventArgs e)
|
||||
{
|
||||
lock (m_AgentList)
|
||||
{
|
||||
if (m_AgentList.ContainsKey(e.SourceID))
|
||||
m_AgentList[e.SourceID] = true;
|
||||
else
|
||||
m_AgentList.Add(e.SourceID, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void Avatars_ViewerEffect(object sender, ViewerEffectEventArgs e)
|
||||
{
|
||||
lock (m_AgentList)
|
||||
{
|
||||
if (m_AgentList.ContainsKey(e.SourceID))
|
||||
m_AgentList[e.SourceID] = true;
|
||||
else
|
||||
m_AgentList.Add(e.SourceID, true);
|
||||
}
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
lock (Client.Network.Simulators)
|
||||
{
|
||||
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||
{
|
||||
Client.Network.Simulators[i].ObjectsAvatars.ForEach(
|
||||
delegate(Avatar av)
|
||||
{
|
||||
lock (m_AgentList)
|
||||
{
|
||||
if (!m_AgentList.ContainsKey(av.ID))
|
||||
{
|
||||
result.AppendLine();
|
||||
result.AppendFormat("{0} (Group: {1}, Location: {2}, UUID: {3} LocalID: {4}) Is Probably a bot",
|
||||
av.Name, av.GroupName, av.Position, av.ID, av.LocalID);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class CloneProfileCommand : Command
|
||||
{
|
||||
Avatar.AvatarProperties Properties;
|
||||
Avatar.Interests Interests;
|
||||
List<UUID> Groups = new List<UUID>();
|
||||
bool ReceivedProperties = false;
|
||||
bool ReceivedInterests = false;
|
||||
bool ReceivedGroups = false;
|
||||
ManualResetEvent ReceivedProfileEvent = new ManualResetEvent(false);
|
||||
|
||||
public CloneProfileCommand(TestClient testClient)
|
||||
{
|
||||
testClient.Avatars.AvatarInterestsReply += new EventHandler<AvatarInterestsReplyEventArgs>(Avatars_AvatarInterestsReply);
|
||||
testClient.Avatars.AvatarPropertiesReply += new EventHandler<AvatarPropertiesReplyEventArgs>(Avatars_AvatarPropertiesReply);
|
||||
testClient.Avatars.AvatarGroupsReply += new EventHandler<AvatarGroupsReplyEventArgs>(Avatars_AvatarGroupsReply);
|
||||
testClient.Groups.GroupJoinedReply += new EventHandler<GroupOperationEventArgs>(Groups_OnGroupJoined);
|
||||
testClient.Avatars.AvatarPicksReply += new EventHandler<AvatarPicksReplyEventArgs>(Avatars_AvatarPicksReply);
|
||||
testClient.Avatars.PickInfoReply += new EventHandler<PickInfoReplyEventArgs>(Avatars_PickInfoReply);
|
||||
|
||||
Name = "cloneprofile";
|
||||
Description = "Clones another avatars profile as closely as possible. WARNING: This command will " +
|
||||
"destroy your existing profile! Usage: cloneprofile [targetuuid]";
|
||||
Category = CommandCategory.Other;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return Description;
|
||||
|
||||
UUID targetID;
|
||||
ReceivedProperties = false;
|
||||
ReceivedInterests = false;
|
||||
ReceivedGroups = false;
|
||||
|
||||
try
|
||||
{
|
||||
targetID = new UUID(args[0]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
|
||||
// Request all of the packets that make up an avatar profile
|
||||
Client.Avatars.RequestAvatarProperties(targetID);
|
||||
|
||||
//Request all of the avatars pics
|
||||
Client.Avatars.RequestAvatarPicks(Client.Self.AgentID);
|
||||
Client.Avatars.RequestAvatarPicks(targetID);
|
||||
|
||||
// Wait for all the packets to arrive
|
||||
ReceivedProfileEvent.Reset();
|
||||
ReceivedProfileEvent.WaitOne(5000, false);
|
||||
|
||||
// Check if everything showed up
|
||||
if (!ReceivedInterests || !ReceivedProperties || !ReceivedGroups)
|
||||
return "Failed to retrieve a complete profile for that UUID";
|
||||
|
||||
// Synchronize our profile
|
||||
Client.Self.UpdateInterests(Interests);
|
||||
Client.Self.UpdateProfile(Properties);
|
||||
|
||||
// TODO: Leave all the groups we're currently a member of? This could
|
||||
// break TestClient connectivity that might be relying on group authentication
|
||||
|
||||
// Attempt to join all the groups
|
||||
foreach (UUID groupID in Groups)
|
||||
{
|
||||
Client.Groups.RequestJoinGroup(groupID);
|
||||
}
|
||||
|
||||
return "Synchronized our profile to the profile of " + targetID.ToString();
|
||||
}
|
||||
|
||||
void Groups_OnGroupJoined(object sender, GroupOperationEventArgs e)
|
||||
{
|
||||
Console.WriteLine(Client.ToString() + (e.Success ? " joined " : " failed to join ") +
|
||||
e.GroupID.ToString());
|
||||
|
||||
if (e.Success)
|
||||
{
|
||||
Console.WriteLine(Client.ToString() + " setting " + e.GroupID.ToString() +
|
||||
" as the active group");
|
||||
Client.Groups.ActivateGroup(e.GroupID);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatars_PickInfoReply(object sender, PickInfoReplyEventArgs e)
|
||||
{
|
||||
Client.Self.PickInfoUpdate(e.PickID, e.Pick.TopPick, e.Pick.ParcelID, e.Pick.Name, e.Pick.PosGlobal, e.Pick.SnapshotID, e.Pick.Desc);
|
||||
}
|
||||
|
||||
void Avatars_AvatarPicksReply(object sender, AvatarPicksReplyEventArgs e)
|
||||
{
|
||||
foreach (KeyValuePair<UUID, string> kvp in e.Picks)
|
||||
{
|
||||
if (e.AvatarID == Client.Self.AgentID)
|
||||
{
|
||||
Client.Self.PickDelete(kvp.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
Client.Avatars.RequestPickInfo(e.AvatarID, kvp.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Avatars_AvatarGroupsReply(object sender, AvatarGroupsReplyEventArgs e)
|
||||
{
|
||||
lock (ReceivedProfileEvent)
|
||||
{
|
||||
foreach (AvatarGroup group in e.Groups)
|
||||
{
|
||||
Groups.Add(group.GroupID);
|
||||
}
|
||||
|
||||
ReceivedGroups = true;
|
||||
|
||||
if (ReceivedInterests && ReceivedProperties && ReceivedGroups)
|
||||
ReceivedProfileEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void Avatars_AvatarPropertiesReply(object sender, AvatarPropertiesReplyEventArgs e)
|
||||
{
|
||||
lock (ReceivedProfileEvent)
|
||||
{
|
||||
Properties = e.Properties;
|
||||
ReceivedProperties = true;
|
||||
|
||||
if (ReceivedInterests && ReceivedProperties && ReceivedGroups)
|
||||
ReceivedProfileEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void Avatars_AvatarInterestsReply(object sender, AvatarInterestsReplyEventArgs e)
|
||||
{
|
||||
lock (ReceivedProfileEvent)
|
||||
{
|
||||
Interests = e.Interests;
|
||||
ReceivedInterests = true;
|
||||
|
||||
if (ReceivedInterests && ReceivedProperties && ReceivedGroups)
|
||||
ReceivedProfileEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a packet of type GenericMessage to the simulator.
|
||||
/// </summary>
|
||||
public class GenericMessageCommand : Command
|
||||
{
|
||||
public GenericMessageCommand(TestClient testClient)
|
||||
{
|
||||
Name = "sendgeneric";
|
||||
Description = "send a generic UDP message to the simulator.";
|
||||
Category = CommandCategory.Other;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
UUID target;
|
||||
|
||||
if (args.Length < 1)
|
||||
return "Usage: sendgeneric method_name [value1 value2 ...]";
|
||||
|
||||
string methodName = args[0];
|
||||
|
||||
GenericMessagePacket gmp = new GenericMessagePacket();
|
||||
|
||||
gmp.AgentData.AgentID = Client.Self.AgentID;
|
||||
gmp.AgentData.SessionID = Client.Self.SessionID;
|
||||
gmp.AgentData.TransactionID = UUID.Zero;
|
||||
|
||||
gmp.MethodData.Method = Utils.StringToBytes(methodName);
|
||||
gmp.MethodData.Invoice = UUID.Zero;
|
||||
|
||||
gmp.ParamList = new GenericMessagePacket.ParamListBlock[args.Length - 1];
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
{
|
||||
GenericMessagePacket.ParamListBlock paramBlock = new GenericMessagePacket.ParamListBlock();
|
||||
paramBlock.Parameter = Utils.StringToBytes(args[i]);
|
||||
gmp.ParamList[i - 1] = paramBlock;
|
||||
sb.AppendFormat(" {0}", args[i]);
|
||||
}
|
||||
|
||||
Client.Network.SendPacket(gmp);
|
||||
|
||||
return string.Format("Sent generic message with method {0}, params{1}", methodName, sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class PlayAnimationCommand : Command
|
||||
{
|
||||
private Dictionary<UUID, string> m_BuiltInAnimations = new Dictionary<UUID, string>(Animations.ToDictionary());
|
||||
public PlayAnimationCommand(TestClient testClient)
|
||||
{
|
||||
Name = "play";
|
||||
Description = "Attempts to play an animation";
|
||||
Category = CommandCategory.Appearance;
|
||||
}
|
||||
|
||||
private string Usage()
|
||||
{
|
||||
String usage = "Usage:\n" +
|
||||
"\tplay list - list the built in animations\n" +
|
||||
"\tplay show - show any currently playing animations\n" +
|
||||
"\tplay UUID - play an animation asset\n" +
|
||||
"\tplay ANIMATION - where ANIMATION is one of the values returned from \"play list\"\n";
|
||||
return usage;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (args.Length != 1)
|
||||
return Usage();
|
||||
|
||||
UUID animationID;
|
||||
string arg = args[0].Trim();
|
||||
|
||||
if (UUID.TryParse(args[0], out animationID))
|
||||
{
|
||||
Client.Self.AnimationStart(animationID, true);
|
||||
}
|
||||
else if (arg.ToLower().Equals("list"))
|
||||
{
|
||||
foreach (string key in m_BuiltInAnimations.Values)
|
||||
{
|
||||
result.AppendLine(key);
|
||||
}
|
||||
}
|
||||
else if (arg.ToLower().Equals("show"))
|
||||
{
|
||||
Client.Self.SignaledAnimations.ForEach(delegate(KeyValuePair<UUID, int> kvp) {
|
||||
if (m_BuiltInAnimations.ContainsKey(kvp.Key))
|
||||
{
|
||||
result.AppendFormat("The {0} System Animation is being played, sequence is {1}", m_BuiltInAnimations[kvp.Key], kvp.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AppendFormat("The {0} Asset Animation is being played, sequence is {0}", kvp.Key, kvp.Value);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (m_BuiltInAnimations.ContainsValue(args[0].Trim().ToUpper()))
|
||||
{
|
||||
foreach (var kvp in m_BuiltInAnimations)
|
||||
{
|
||||
if (kvp.Value.Equals(arg.ToUpper()))
|
||||
{
|
||||
Client.Self.AnimationStart(kvp.Key, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Usage();
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class TouchCommand: Command
|
||||
{
|
||||
public TouchCommand(TestClient testClient)
|
||||
{
|
||||
Name = "touch";
|
||||
Description = "Attempt to touch a prim with specified UUID";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
UUID target;
|
||||
|
||||
if (args.Length != 1)
|
||||
return "Usage: touch UUID";
|
||||
|
||||
if (UUID.TryParse(args[0], out target))
|
||||
{
|
||||
Primitive targetPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find(
|
||||
delegate(Primitive prim)
|
||||
{
|
||||
return prim.ID == target;
|
||||
}
|
||||
);
|
||||
|
||||
if (targetPrim != null)
|
||||
{
|
||||
Client.Self.Touch(targetPrim.LocalID);
|
||||
return "Touched prim " + targetPrim.LocalID;
|
||||
}
|
||||
}
|
||||
|
||||
return "Couldn't find a prim to touch with UUID " + args[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class WhoCommand: Command
|
||||
{
|
||||
public WhoCommand(TestClient testClient)
|
||||
{
|
||||
Name = "who";
|
||||
Description = "Lists seen avatars.";
|
||||
Category = CommandCategory.Other;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
lock (Client.Network.Simulators)
|
||||
{
|
||||
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||
{
|
||||
Client.Network.Simulators[i].ObjectsAvatars.ForEach(
|
||||
delegate(Avatar av)
|
||||
{
|
||||
result.AppendLine();
|
||||
result.AppendFormat("{0} (Group: {1}, Location: {2}, UUID: {3} LocalID: {4})",
|
||||
av.Name, av.GroupName, av.Position, av.ID, av.LocalID);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Set avatars current appearance to appearance last stored on simulator
|
||||
/// </summary>
|
||||
public class AppearanceCommand : Command
|
||||
{
|
||||
public AppearanceCommand(TestClient testClient)
|
||||
{
|
||||
Name = "appearance";
|
||||
Description = "Set your current appearance to your last saved appearance. Usage: appearance [rebake]";
|
||||
Category = CommandCategory.Appearance;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Appearance.RequestSetAppearance((args.Length > 0 && args[0].Equals("rebake")));
|
||||
return "Appearance sequence started";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class AttachmentsCommand : Command
|
||||
{
|
||||
public AttachmentsCommand(TestClient testClient)
|
||||
{
|
||||
Client = testClient;
|
||||
Name = "attachments";
|
||||
Description = "Prints a list of the currently known agent attachments";
|
||||
Category = CommandCategory.Appearance;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
List<Primitive> attachments = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(
|
||||
delegate(Primitive prim) { return prim.ParentID == Client.Self.LocalID; }
|
||||
);
|
||||
|
||||
for (int i = 0; i < attachments.Count; i++)
|
||||
{
|
||||
Primitive prim = attachments[i];
|
||||
AttachmentPoint point = StateToAttachmentPoint(prim.PrimData.State);
|
||||
|
||||
// TODO: Fetch properties for the objects with missing property sets so we can show names
|
||||
Logger.Log(String.Format("[Attachment @ {0}] LocalID: {1} UUID: {2} Offset: {3}",
|
||||
point, prim.LocalID, prim.ID, prim.Position), Helpers.LogLevel.Info, Client);
|
||||
}
|
||||
|
||||
return "Found " + attachments.Count + " attachments";
|
||||
}
|
||||
|
||||
public static AttachmentPoint StateToAttachmentPoint(uint state)
|
||||
{
|
||||
const uint ATTACHMENT_MASK = 0xF0;
|
||||
uint fixedState = (((byte)state & ATTACHMENT_MASK) >> 4) | (((byte)state & ~ATTACHMENT_MASK) << 4);
|
||||
return (AttachmentPoint)fixedState;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Appearance
|
||||
{
|
||||
public class AvatarInfoCommand : Command
|
||||
{
|
||||
public AvatarInfoCommand(TestClient testClient)
|
||||
{
|
||||
Name = "avatarinfo";
|
||||
Description = "Print out information on a nearby avatar. Usage: avatarinfo [firstname] [lastname]";
|
||||
Category = CommandCategory.Appearance;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
return "Usage: avatarinfo [firstname] [lastname]";
|
||||
|
||||
string targetName = String.Format("{0} {1}", args[0], args[1]);
|
||||
|
||||
Avatar foundAv = Client.Network.CurrentSim.ObjectsAvatars.Find(
|
||||
delegate(Avatar avatar) { return (avatar.Name == targetName); }
|
||||
);
|
||||
|
||||
if (foundAv != null)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
output.AppendFormat("{0} ({1})", targetName, foundAv.ID);
|
||||
output.AppendLine();
|
||||
|
||||
for (int i = 0; i < foundAv.Textures.FaceTextures.Length; i++)
|
||||
{
|
||||
if (foundAv.Textures.FaceTextures[i] != null)
|
||||
{
|
||||
Primitive.TextureEntryFace face = foundAv.Textures.FaceTextures[i];
|
||||
AvatarTextureIndex type = (AvatarTextureIndex)i;
|
||||
|
||||
output.AppendFormat("{0}: {1}", type, face.TextureID);
|
||||
output.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "No nearby avatar with the name " + targetName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class CloneCommand : Command
|
||||
{
|
||||
uint SerialNum = 2;
|
||||
|
||||
public CloneCommand(TestClient testClient)
|
||||
{
|
||||
Name = "clone";
|
||||
Description = "Clone the appearance of a nearby avatar. Usage: clone [name]";
|
||||
Category = CommandCategory.Appearance;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
string targetName = String.Empty;
|
||||
List<DirectoryManager.AgentSearchData> matches;
|
||||
|
||||
for (int ct = 0; ct < args.Length; ct++)
|
||||
targetName = targetName + args[ct] + " ";
|
||||
targetName = targetName.TrimEnd();
|
||||
|
||||
if (targetName.Length == 0)
|
||||
return "Usage: clone [name]";
|
||||
|
||||
if (Client.Directory.PeopleSearch(DirectoryManager.DirFindFlags.People, targetName, 0, 1000 * 10,
|
||||
out matches) && matches.Count > 0)
|
||||
{
|
||||
UUID target = matches[0].AgentID;
|
||||
targetName += String.Format(" ({0})", target);
|
||||
|
||||
if (Client.Appearances.ContainsKey(target))
|
||||
{
|
||||
#region AvatarAppearance to AgentSetAppearance
|
||||
|
||||
AvatarAppearancePacket appearance = Client.Appearances[target];
|
||||
|
||||
AgentSetAppearancePacket set = new AgentSetAppearancePacket();
|
||||
set.AgentData.AgentID = Client.Self.AgentID;
|
||||
set.AgentData.SessionID = Client.Self.SessionID;
|
||||
set.AgentData.SerialNum = SerialNum++;
|
||||
set.AgentData.Size = new Vector3(2f, 2f, 2f); // HACK
|
||||
|
||||
set.WearableData = new AgentSetAppearancePacket.WearableDataBlock[0];
|
||||
set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[appearance.VisualParam.Length];
|
||||
|
||||
for (int i = 0; i < appearance.VisualParam.Length; i++)
|
||||
{
|
||||
set.VisualParam[i] = new AgentSetAppearancePacket.VisualParamBlock();
|
||||
set.VisualParam[i].ParamValue = appearance.VisualParam[i].ParamValue;
|
||||
}
|
||||
|
||||
set.ObjectData.TextureEntry = appearance.ObjectData.TextureEntry;
|
||||
|
||||
#endregion AvatarAppearance to AgentSetAppearance
|
||||
|
||||
// Detach everything we are currently wearing
|
||||
Client.Appearance.AddAttachments(new List<InventoryItem>(), true);
|
||||
|
||||
// Send the new appearance packet
|
||||
Client.Network.SendPacket(set);
|
||||
|
||||
return "Cloned " + targetName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Don't know the appearance of avatar " + targetName;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Couldn't find avatar " + targetName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class WearCommand : Command
|
||||
{
|
||||
public WearCommand(TestClient testClient)
|
||||
{
|
||||
Client = testClient;
|
||||
Name = "wear";
|
||||
Description = "Wear an outfit folder from inventory. Usage: wear [outfit name]";
|
||||
Category = CommandCategory.Appearance;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: wear [outfit name] eg: 'wear Clothing/My Outfit";
|
||||
|
||||
string target = String.Empty;
|
||||
|
||||
for (int ct = 0; ct < args.Length; ct++)
|
||||
{
|
||||
target += args[ct] + " ";
|
||||
}
|
||||
|
||||
target = target.TrimEnd();
|
||||
|
||||
UUID folder = Client.Inventory.FindObjectByPath(Client.Inventory.Store.RootFolder.UUID, Client.Self.AgentID, target, 20 * 1000);
|
||||
|
||||
if (folder == UUID.Zero)
|
||||
{
|
||||
return "Outfit path " + target + " not found";
|
||||
}
|
||||
|
||||
List<InventoryBase> contents = Client.Inventory.FolderContents(folder, Client.Self.AgentID, true, true, InventorySortOrder.ByName, 20 * 1000);
|
||||
List<InventoryItem> items = new List<InventoryItem>();
|
||||
|
||||
if (contents == null)
|
||||
{
|
||||
return "Failed to get contents of " + target;
|
||||
}
|
||||
|
||||
foreach (InventoryBase item in contents)
|
||||
{
|
||||
if (item is InventoryItem)
|
||||
items.Add((InventoryItem)item);
|
||||
}
|
||||
|
||||
Client.Appearance.ReplaceOutfit(items);
|
||||
|
||||
return "Starting to change outfit to " + target;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class EchoMasterCommand: Command
|
||||
{
|
||||
public EchoMasterCommand(TestClient testClient)
|
||||
{
|
||||
Name = "echoMaster";
|
||||
Description = "Repeat everything that master says.";
|
||||
Category = CommandCategory.Communication;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (!Active)
|
||||
{
|
||||
Active = true;
|
||||
Client.Self.ChatFromSimulator += Self_ChatFromSimulator;
|
||||
return "Echoing is now on.";
|
||||
}
|
||||
else
|
||||
{
|
||||
Active = false;
|
||||
Client.Self.ChatFromSimulator -= Self_ChatFromSimulator;
|
||||
return "Echoing is now off.";
|
||||
}
|
||||
}
|
||||
|
||||
void Self_ChatFromSimulator(object sender, ChatEventArgs e)
|
||||
{
|
||||
if (e.Message.Length > 0 && (Client.MasterKey == e.SourceID || (Client.MasterName == e.FromName && !Client.AllowObjectMaster)))
|
||||
Client.Self.Chat(e.Message, 0, ChatType.Normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ImCommand : Command
|
||||
{
|
||||
string ToAvatarName = String.Empty;
|
||||
ManualResetEvent NameSearchEvent = new ManualResetEvent(false);
|
||||
Dictionary<string, UUID> Name2Key = new Dictionary<string, UUID>();
|
||||
|
||||
public ImCommand(TestClient testClient)
|
||||
{
|
||||
testClient.Avatars.AvatarPickerReply += Avatars_AvatarPickerReply;
|
||||
|
||||
Name = "im";
|
||||
Description = "Instant message someone. Usage: im [firstname] [lastname] [message]";
|
||||
Category = CommandCategory.Communication;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 3)
|
||||
return "Usage: im [firstname] [lastname] [message]";
|
||||
|
||||
ToAvatarName = args[0] + " " + args[1];
|
||||
|
||||
// Build the message
|
||||
string message = String.Empty;
|
||||
for (int ct = 2; ct < args.Length; ct++)
|
||||
message += args[ct] + " ";
|
||||
message = message.TrimEnd();
|
||||
if (message.Length > 1023) message = message.Remove(1023);
|
||||
|
||||
if (!Name2Key.ContainsKey(ToAvatarName.ToLower()))
|
||||
{
|
||||
// Send the Query
|
||||
Client.Avatars.RequestAvatarNameSearch(ToAvatarName, UUID.Random());
|
||||
|
||||
NameSearchEvent.WaitOne(6000, false);
|
||||
}
|
||||
|
||||
if (Name2Key.ContainsKey(ToAvatarName.ToLower()))
|
||||
{
|
||||
UUID id = Name2Key[ToAvatarName.ToLower()];
|
||||
|
||||
Client.Self.InstantMessage(id, message);
|
||||
return "Instant Messaged " + id.ToString() + " with message: " + message;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Name lookup for " + ToAvatarName + " failed";
|
||||
}
|
||||
}
|
||||
|
||||
void Avatars_AvatarPickerReply(object sender, AvatarPickerReplyEventArgs e)
|
||||
{
|
||||
foreach (KeyValuePair<UUID, string> kvp in e.Avatars)
|
||||
{
|
||||
if (kvp.Value.ToLower() == ToAvatarName.ToLower())
|
||||
{
|
||||
Name2Key[ToAvatarName.ToLower()] = kvp.Key;
|
||||
NameSearchEvent.Set();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ImGroupCommand : Command
|
||||
{
|
||||
UUID ToGroupID = UUID.Zero;
|
||||
ManualResetEvent WaitForSessionStart = new ManualResetEvent(false);
|
||||
public ImGroupCommand(TestClient testClient)
|
||||
{
|
||||
|
||||
Name = "imgroup";
|
||||
Description = "Send an instant message to a group. Usage: imgroup [group_uuid] [message]";
|
||||
Category = CommandCategory.Communication;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
return "Usage: imgroup [group_uuid] [message]";
|
||||
|
||||
|
||||
|
||||
if (UUID.TryParse(args[0], out ToGroupID))
|
||||
{
|
||||
string message = String.Empty;
|
||||
for (int ct = 1; ct < args.Length; ct++)
|
||||
message += args[ct] + " ";
|
||||
|
||||
message = message.TrimEnd();
|
||||
if (message.Length > 1023)
|
||||
{
|
||||
message = message.Remove(1023);
|
||||
Console.WriteLine("Message truncated at 1024 characters");
|
||||
}
|
||||
|
||||
Client.Self.GroupChatJoined += Self_GroupChatJoined;
|
||||
|
||||
if (!Client.Self.GroupChatSessions.ContainsKey(ToGroupID))
|
||||
{
|
||||
WaitForSessionStart.Reset();
|
||||
Client.Self.RequestJoinGroupChat(ToGroupID);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForSessionStart.Set();
|
||||
}
|
||||
|
||||
if (WaitForSessionStart.WaitOne(20000, false))
|
||||
{
|
||||
Client.Self.InstantMessageGroup(ToGroupID, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Timeout waiting for group session start";
|
||||
}
|
||||
|
||||
Client.Self.GroupChatJoined -= Self_GroupChatJoined;
|
||||
return "Instant Messaged group " + ToGroupID.ToString() + " with message: " + message;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "failed to instant message group";
|
||||
}
|
||||
}
|
||||
|
||||
void Self_GroupChatJoined(object sender, GroupChatJoinedEventArgs e)
|
||||
{
|
||||
if (e.Success)
|
||||
{
|
||||
Console.WriteLine("Joined {0} Group Chat Success!", e.SessionName);
|
||||
WaitForSessionStart.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Join Group Chat failed :(");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class SayCommand: Command
|
||||
{
|
||||
public SayCommand(TestClient testClient)
|
||||
{
|
||||
Name = "say";
|
||||
Description = "Say something. (usage: say (optional channel) whatever)";
|
||||
Category = CommandCategory.Communication;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
int channel = 0;
|
||||
int startIndex = 0;
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
return "usage: say (optional channel) whatever";
|
||||
}
|
||||
else if (args.Length > 1)
|
||||
{
|
||||
if (Int32.TryParse(args[0], out channel))
|
||||
startIndex = 1;
|
||||
}
|
||||
|
||||
String message = String.Empty;
|
||||
|
||||
for (int i = startIndex; i < args.Length; i++)
|
||||
{
|
||||
// Append a space before the next arg
|
||||
if( i > 0 )
|
||||
message += " ";
|
||||
message += args[i];
|
||||
}
|
||||
|
||||
Client.Self.Chat(message, channel, ChatType.Normal);
|
||||
|
||||
return "Said " + message.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ShoutCommand : Command
|
||||
{
|
||||
public ShoutCommand(TestClient testClient)
|
||||
{
|
||||
Name = "shout";
|
||||
Description = "Shout something.";
|
||||
Category = CommandCategory.Communication;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
int channel = 0;
|
||||
int startIndex = 0;
|
||||
string message = String.Empty;
|
||||
if (args.Length < 1)
|
||||
{
|
||||
return "usage: shout (optional channel) whatever";
|
||||
}
|
||||
else if (args.Length > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel = Convert.ToInt32(args[0]);
|
||||
startIndex = 1;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
channel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = startIndex; i < args.Length; i++)
|
||||
{
|
||||
// Append a space before the next arg
|
||||
if( i > 0 )
|
||||
message += " ";
|
||||
message += args[i];
|
||||
}
|
||||
|
||||
Client.Self.Chat(message, channel, ChatType.Shout);
|
||||
|
||||
return "Shouted " + message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class WhisperCommand : Command
|
||||
{
|
||||
public WhisperCommand(TestClient testClient)
|
||||
{
|
||||
Name = "whisper";
|
||||
Description = "Whisper something.";
|
||||
Category = CommandCategory.Communication;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
int channel = 0;
|
||||
int startIndex = 0;
|
||||
string message = String.Empty;
|
||||
if (args.Length < 1)
|
||||
{
|
||||
return "usage: whisper (optional channel) whatever";
|
||||
}
|
||||
else if (args.Length > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel = Convert.ToInt32(args[0]);
|
||||
startIndex = 1;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
channel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = startIndex; i < args.Length; i++)
|
||||
{
|
||||
// Append a space before the next arg
|
||||
if( i > 0 )
|
||||
message += " ";
|
||||
message += args[i];
|
||||
}
|
||||
|
||||
Client.Self.Chat(message, channel, ChatType.Whisper);
|
||||
|
||||
return "Whispered " + message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class key2nameCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
StringBuilder result = new StringBuilder();
|
||||
public key2nameCommand(TestClient testClient)
|
||||
{
|
||||
Name = "key2name";
|
||||
Description = "resolve a UUID to an avatar or group name. Usage: key2name UUID";
|
||||
Category = CommandCategory.Search;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: key2name UUID";
|
||||
|
||||
UUID key;
|
||||
if(!UUID.TryParse(args[0].Trim(), out key))
|
||||
{
|
||||
return "UUID " + args[0].Trim() + " appears to be invalid";
|
||||
}
|
||||
result.Remove(0, result.Length);
|
||||
waitQuery.Reset();
|
||||
|
||||
Client.Avatars.UUIDNameReply += Avatars_OnAvatarNames;
|
||||
Client.Groups.GroupProfile += Groups_OnGroupProfile;
|
||||
Client.Avatars.RequestAvatarName(key);
|
||||
|
||||
Client.Groups.RequestGroupProfile(key);
|
||||
if (!waitQuery.WaitOne(10000, false))
|
||||
{
|
||||
result.AppendLine("Timeout waiting for reply, this could mean the Key is not an avatar or a group");
|
||||
}
|
||||
|
||||
Client.Avatars.UUIDNameReply -= Avatars_OnAvatarNames;
|
||||
Client.Groups.GroupProfile -= Groups_OnGroupProfile;
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
void Groups_OnGroupProfile(object sender, GroupProfileEventArgs e)
|
||||
{
|
||||
result.AppendLine("Group: " + e.Group.Name + " " + e.Group.ID);
|
||||
waitQuery.Set();
|
||||
}
|
||||
|
||||
void Avatars_OnAvatarNames(object sender, UUIDNameReplyEventArgs e)
|
||||
{
|
||||
foreach (KeyValuePair<UUID, string> kvp in e.Names)
|
||||
result.AppendLine("Avatar: " + kvp.Value + " " + kvp.Key);
|
||||
waitQuery.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class SearchClassifiedsCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
|
||||
public SearchClassifiedsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "searchclassifieds";
|
||||
Description = "Searches Classified Ads. Usage: searchclassifieds [search text]";
|
||||
Category = CommandCategory.Search;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: searchclassifieds [search text]";
|
||||
|
||||
string searchText = string.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
searchText += args[i] + " ";
|
||||
searchText = searchText.TrimEnd();
|
||||
waitQuery.Reset();
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
EventHandler<DirClassifiedsReplyEventArgs> callback = delegate(object sender, DirClassifiedsReplyEventArgs e)
|
||||
{
|
||||
result.AppendFormat("Your search string '{0}' returned {1} classified ads" + System.Environment.NewLine,
|
||||
searchText, e.Classifieds.Count);
|
||||
foreach (DirectoryManager.Classified ad in e.Classifieds)
|
||||
{
|
||||
result.AppendLine(ad.ToString());
|
||||
}
|
||||
|
||||
// classifieds are sent 16 ads at a time
|
||||
if (e.Classifieds.Count < 16)
|
||||
{
|
||||
waitQuery.Set();
|
||||
}
|
||||
};
|
||||
|
||||
Client.Directory.DirClassifiedsReply += callback;
|
||||
|
||||
UUID searchID = Client.Directory.StartClassifiedSearch(searchText, DirectoryManager.ClassifiedCategories.Any, DirectoryManager.ClassifiedQueryFlags.Mature | DirectoryManager.ClassifiedQueryFlags.PG);
|
||||
|
||||
if (!waitQuery.WaitOne(20000, false) && Client.Network.Connected)
|
||||
{
|
||||
result.AppendLine("Timeout waiting for simulator to respond to query.");
|
||||
}
|
||||
|
||||
Client.Directory.DirClassifiedsReply -= callback;
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class SearchEventsCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
int resultCount;
|
||||
|
||||
public SearchEventsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "searchevents";
|
||||
Description = "Searches Events list. Usage: searchevents [search text]";
|
||||
Category = CommandCategory.Search;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// process command line arguments
|
||||
if (args.Length < 1)
|
||||
return "Usage: searchevents [search text]";
|
||||
|
||||
string searchText = string.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
searchText += args[i] + " ";
|
||||
searchText = searchText.TrimEnd();
|
||||
|
||||
waitQuery.Reset();
|
||||
|
||||
Client.Directory.DirEventsReply += Directory_DirEvents;
|
||||
|
||||
// send the request to the directory manager
|
||||
Client.Directory.StartEventsSearch(searchText, 0);
|
||||
string result;
|
||||
if (waitQuery.WaitOne(20000, false) && Client.Network.Connected)
|
||||
{
|
||||
result = "Your query '" + searchText + "' matched " + resultCount + " Events. ";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "Timeout waiting for simulator to respond.";
|
||||
}
|
||||
|
||||
Client.Directory.DirEventsReply -= Directory_DirEvents;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Directory_DirEvents(object sender, DirEventsReplyEventArgs e)
|
||||
{
|
||||
if (e.MatchedEvents[0].ID == 0 && e.MatchedEvents.Count == 1)
|
||||
{
|
||||
Console.WriteLine("No Results matched your search string");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DirectoryManager.EventsSearchData ev in e.MatchedEvents)
|
||||
{
|
||||
Console.WriteLine("Event ID: {0} Event Name: {1} Event Date: {2}", ev.ID, ev.Name, ev.Date);
|
||||
}
|
||||
}
|
||||
resultCount = e.MatchedEvents.Count;
|
||||
waitQuery.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class SearchGroupsCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
int resultCount = 0;
|
||||
|
||||
public SearchGroupsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "searchgroups";
|
||||
Description = "Searches groups. Usage: searchgroups [search text]";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// process command line arguments
|
||||
if (args.Length < 1)
|
||||
return "Usage: searchgroups [search text]";
|
||||
|
||||
string searchText = string.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
searchText += args[i] + " ";
|
||||
searchText = searchText.TrimEnd();
|
||||
|
||||
waitQuery.Reset();
|
||||
|
||||
Client.Directory.DirGroupsReply += Directory_DirGroups;
|
||||
|
||||
// send the request to the directory manager
|
||||
Client.Directory.StartGroupSearch(searchText, 0);
|
||||
|
||||
string result;
|
||||
if (waitQuery.WaitOne(20000, false) && Client.Network.Connected)
|
||||
{
|
||||
result = "Your query '" + searchText + "' matched " + resultCount + " Groups. ";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "Timeout waiting for simulator to respond.";
|
||||
}
|
||||
|
||||
Client.Directory.DirGroupsReply -= Directory_DirGroups;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Directory_DirGroups(object sender, DirGroupsReplyEventArgs e)
|
||||
{
|
||||
if (e.MatchedGroups.Count > 0)
|
||||
{
|
||||
foreach (DirectoryManager.GroupSearchData group in e.MatchedGroups)
|
||||
{
|
||||
Console.WriteLine("Group {1} ({0}) has {2} members", group.GroupID, group.GroupName, group.Members);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Didn't find any groups that matched your query :(");
|
||||
}
|
||||
waitQuery.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class SearchLandCommand : Command
|
||||
{
|
||||
private System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
private StringBuilder result = new StringBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new instance of the SearchLandCommand
|
||||
/// </summary>
|
||||
/// <param name="testClient"></param>
|
||||
public SearchLandCommand(TestClient testClient)
|
||||
{
|
||||
Name = "searchland";
|
||||
Description = "Searches for land for sale. for usage information type: searchland";
|
||||
Category = CommandCategory.Search;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show commandusage
|
||||
/// </summary>
|
||||
/// <returns>A string containing the parameter usage instructions</returns>
|
||||
public string ShowUsage()
|
||||
{
|
||||
return "Usage: searchland [type] [max price] [min size]\n" +
|
||||
"\twhere [type] is one of: mainland, auction, estate, all\n" +
|
||||
"\tif [max price] or [min size] are 0 that parameter will be ignored\n\n" +
|
||||
"example: \"searchland mainland 0 512\" // shows the lowest priced mainland that is larger than 512/m2\n\n";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="fromAgentID"></param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// process command line arguments
|
||||
if (args.Length < 3)
|
||||
return ShowUsage();
|
||||
|
||||
string searchType = args[0].Trim().ToLower();
|
||||
int maxPrice;
|
||||
int minSize;
|
||||
|
||||
DirectoryManager.SearchTypeFlags searchTypeFlags = DirectoryManager.SearchTypeFlags.Any;
|
||||
|
||||
if (searchType.StartsWith("au"))
|
||||
searchTypeFlags = DirectoryManager.SearchTypeFlags.Auction;
|
||||
else if (searchType.StartsWith("m"))
|
||||
searchTypeFlags = DirectoryManager.SearchTypeFlags.Mainland;
|
||||
else if (searchType.StartsWith("e"))
|
||||
searchTypeFlags = DirectoryManager.SearchTypeFlags.Estate;
|
||||
else if (searchType.StartsWith("al"))
|
||||
searchTypeFlags = DirectoryManager.SearchTypeFlags.Any;
|
||||
else
|
||||
return ShowUsage();
|
||||
|
||||
// initialize some default flags we'll use in the search
|
||||
DirectoryManager.DirFindFlags queryFlags = DirectoryManager.DirFindFlags.SortAsc | DirectoryManager.DirFindFlags.PerMeterSort
|
||||
| DirectoryManager.DirFindFlags.IncludeAdult | DirectoryManager.DirFindFlags.IncludePG | DirectoryManager.DirFindFlags.IncludeMature;
|
||||
|
||||
// validate the parameters passed
|
||||
if (int.TryParse(args[1], out maxPrice) && int.TryParse(args[2], out minSize))
|
||||
{
|
||||
// if the [max price] parameter is greater than 0, we'll enable the flag to limit by price
|
||||
if (maxPrice > 0)
|
||||
queryFlags |= DirectoryManager.DirFindFlags.LimitByPrice;
|
||||
|
||||
// if the [min size] parameter is greater than 0, we'll enable the flag to limit by area
|
||||
if (minSize > 0)
|
||||
queryFlags |= DirectoryManager.DirFindFlags.LimitByArea;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ShowUsage();
|
||||
}
|
||||
|
||||
//waitQuery.Reset();
|
||||
|
||||
// subscribe to the event that returns the search results
|
||||
Client.Directory.DirLandReply += Directory_DirLand;
|
||||
|
||||
// send the request to the directory manager
|
||||
Client.Directory.StartLandSearch(queryFlags, searchTypeFlags, maxPrice, minSize, 0);
|
||||
|
||||
if (!waitQuery.WaitOne(20000, false) && Client.Network.Connected)
|
||||
{
|
||||
result.AppendLine("Timeout waiting for simulator to respond.");
|
||||
}
|
||||
|
||||
// unsubscribe to the event that returns the search results
|
||||
Client.Directory.DirLandReply -= Directory_DirLand;
|
||||
|
||||
// return the results
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the search reply
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Directory_DirLand(object sender, DirLandReplyEventArgs e)
|
||||
{
|
||||
|
||||
foreach (DirectoryManager.DirectoryParcel searchResult in e.DirParcels)
|
||||
{
|
||||
// add the results to the StringBuilder object that contains the results
|
||||
result.AppendLine(searchResult.ToString());
|
||||
}
|
||||
result.AppendFormat("{0} results" + System.Environment.NewLine, e.DirParcels.Count);
|
||||
// let the calling method know we have data
|
||||
waitQuery.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class SearchPeopleCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
int resultCount = 0;
|
||||
|
||||
public SearchPeopleCommand(TestClient testClient)
|
||||
{
|
||||
Name = "searchpeople";
|
||||
Description = "Searches for other avatars. Usage: searchpeople [search text]";
|
||||
Category = CommandCategory.Search;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// process command line arguments
|
||||
if (args.Length < 1)
|
||||
return "Usage: searchpeople [search text]";
|
||||
|
||||
string searchText = string.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
searchText += args[i] + " ";
|
||||
searchText = searchText.TrimEnd();
|
||||
|
||||
waitQuery.Reset();
|
||||
|
||||
|
||||
Client.Directory.DirPeopleReply += Directory_DirPeople;
|
||||
|
||||
// send the request to the directory manager
|
||||
Client.Directory.StartPeopleSearch(searchText, 0);
|
||||
|
||||
string result;
|
||||
if (waitQuery.WaitOne(20000, false) && Client.Network.Connected)
|
||||
{
|
||||
result = "Your query '" + searchText + "' matched " + resultCount + " People. ";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "Timeout waiting for simulator to respond.";
|
||||
}
|
||||
|
||||
Client.Directory.DirPeopleReply -= Directory_DirPeople;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Directory_DirPeople(object sender, DirPeopleReplyEventArgs e)
|
||||
{
|
||||
if (e.MatchedPeople.Count > 0)
|
||||
{
|
||||
foreach (DirectoryManager.AgentSearchData agent in e.MatchedPeople)
|
||||
{
|
||||
Console.WriteLine("{0} {1} ({2})", agent.FirstName, agent.LastName, agent.AgentID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Didn't find any people that matched your query :(");
|
||||
}
|
||||
waitQuery.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class SearchPlacesCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false);
|
||||
|
||||
public SearchPlacesCommand(TestClient testClient)
|
||||
{
|
||||
Name = "searchplaces";
|
||||
Description = "Searches Places. Usage: searchplaces [search text]";
|
||||
Category = CommandCategory.Search;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: searchplaces [search text]";
|
||||
|
||||
string searchText = string.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
searchText += args[i] + " ";
|
||||
searchText = searchText.TrimEnd();
|
||||
waitQuery.Reset();
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
EventHandler<PlacesReplyEventArgs> callback = delegate(object sender, PlacesReplyEventArgs e)
|
||||
{
|
||||
result.AppendFormat("Your search string '{0}' returned {1} results" + System.Environment.NewLine,
|
||||
searchText, e.MatchedPlaces.Count);
|
||||
foreach (DirectoryManager.PlacesSearchData place in e.MatchedPlaces)
|
||||
{
|
||||
result.AppendLine(place.ToString());
|
||||
}
|
||||
|
||||
waitQuery.Set();
|
||||
};
|
||||
|
||||
Client.Directory.PlacesReply += callback;
|
||||
Client.Directory.StartPlacesSearch(searchText);
|
||||
|
||||
if (!waitQuery.WaitOne(20000, false) && Client.Network.Connected)
|
||||
{
|
||||
result.AppendLine("Timeout waiting for simulator to respond to query.");
|
||||
}
|
||||
|
||||
Client.Directory.PlacesReply -= callback;
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands
|
||||
{
|
||||
class ShowEventDetailsCommand : Command
|
||||
{
|
||||
public ShowEventDetailsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "showevent";
|
||||
Description = "Shows an Events details. Usage: showevent [eventID]";
|
||||
Category = CommandCategory.Other;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: showevent [eventID] (use searchevents to get ID)";
|
||||
|
||||
Client.Directory.EventInfoReply += Directory_EventDetails;
|
||||
uint eventID;
|
||||
|
||||
if (UInt32.TryParse(args[0], out eventID))
|
||||
{
|
||||
Client.Directory.EventInfoRequest(eventID);
|
||||
return "Sent query for Event " + eventID;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Usage: showevent [eventID] (use searchevents to get ID)";
|
||||
}
|
||||
}
|
||||
|
||||
void Directory_EventDetails(object sender, EventInfoReplyEventArgs e)
|
||||
{
|
||||
float x, y;
|
||||
Helpers.GlobalPosToRegionHandle((float)e.MatchedEvent.GlobalPos.X, (float)e.MatchedEvent.GlobalPos.Y, out x, out y);
|
||||
StringBuilder sb = new StringBuilder("secondlife://" + e.MatchedEvent.SimName + "/" + x + "/" + y + "/0" + System.Environment.NewLine);
|
||||
sb.AppendLine(e.MatchedEvent.ToString());
|
||||
|
||||
//sb.AppendFormat(" Name: {0} ({1})" + System.Environment.NewLine, e.MatchedEvent.Name, e.MatchedEvent.ID);
|
||||
//sb.AppendFormat(" Location: {0}/{1}/{2}" + System.Environment.NewLine, e.MatchedEvent.SimName, x, y);
|
||||
//sb.AppendFormat(" Date: {0}" + System.Environment.NewLine, e.MatchedEvent.Date);
|
||||
//sb.AppendFormat("Description: {0}" + System.Environment.NewLine, e.MatchedEvent.Desc);
|
||||
Console.WriteLine(sb.ToString());
|
||||
Client.Directory.EventInfoReply -= Directory_EventDetails;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Request the raw terrain file from the simulator, save it as a file.
|
||||
///
|
||||
/// Can only be used by the Estate Owner
|
||||
/// </summary>
|
||||
public class DownloadTerrainCommand : Command
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a Synchronization event object
|
||||
/// </summary>
|
||||
private static AutoResetEvent xferTimeout = new AutoResetEvent(false);
|
||||
|
||||
/// <summary>A string we use to report the result of the request with.</summary>
|
||||
private static System.Text.StringBuilder result = new System.Text.StringBuilder();
|
||||
|
||||
private static string fileName;
|
||||
|
||||
/// <summary>
|
||||
/// Download a simulators raw terrain data and save it to a file
|
||||
/// </summary>
|
||||
/// <param name="testClient"></param>
|
||||
public DownloadTerrainCommand(TestClient testClient)
|
||||
{
|
||||
Name = "downloadterrain";
|
||||
Description = "Download the RAW terrain file for this estate. Usage: downloadterrain [timeout]";
|
||||
Category = CommandCategory.Simulator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the application
|
||||
/// </summary>
|
||||
/// <param name="args">arguments passed to this module</param>
|
||||
/// <param name="fromAgentID">The ID of the avatar sending the request</param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
int timeout = 120000; // default the timeout to 2 minutes
|
||||
fileName = Client.Network.CurrentSim.Name + ".raw";
|
||||
|
||||
if (args.Length > 0 && int.TryParse(args[0], out timeout) != true)
|
||||
return "Usage: downloadterrain [timeout]";
|
||||
|
||||
// Create a delegate which will be fired when the simulator receives our download request
|
||||
// Starts the actual transfer request
|
||||
EventHandler<InitiateDownloadEventArgs> initiateDownloadDelegate =
|
||||
delegate(object sender, InitiateDownloadEventArgs e)
|
||||
{
|
||||
Client.Assets.RequestAssetXfer(e.SimFileName, false, false, UUID.Zero, AssetType.Unknown, false);
|
||||
};
|
||||
|
||||
// Subscribe to the event that will tell us the status of the download
|
||||
Client.Assets.XferReceived += new EventHandler<XferReceivedEventArgs>(Assets_XferReceived);
|
||||
// subscribe to the event which tells us when the simulator has received our request
|
||||
Client.Assets.InitiateDownload += initiateDownloadDelegate;
|
||||
|
||||
// configure request to tell the simulator to send us the file
|
||||
List<string> parameters = new List<string>();
|
||||
parameters.Add("download filename");
|
||||
parameters.Add(fileName);
|
||||
// send the request
|
||||
Client.Estate.EstateOwnerMessage("terrain", parameters);
|
||||
|
||||
// wait for (timeout) seconds for the request to complete (defaults 2 minutes)
|
||||
if (!xferTimeout.WaitOne(timeout, false))
|
||||
{
|
||||
result.Append("Timeout while waiting for terrain data");
|
||||
}
|
||||
|
||||
// unsubscribe from events
|
||||
Client.Assets.InitiateDownload -= initiateDownloadDelegate;
|
||||
Client.Assets.XferReceived -= new EventHandler<XferReceivedEventArgs>(Assets_XferReceived);
|
||||
|
||||
// return the result
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the reply to the OnXferReceived event
|
||||
/// </summary>
|
||||
private void Assets_XferReceived(object sender, XferReceivedEventArgs e)
|
||||
{
|
||||
if (e.Xfer.Success)
|
||||
{
|
||||
// set the result message
|
||||
result.AppendFormat("Terrain file {0} ({1} bytes) downloaded successfully, written to {2}", e.Xfer.Filename, e.Xfer.Size, fileName);
|
||||
|
||||
// write the file to disk
|
||||
FileStream stream = new FileStream(fileName, FileMode.Create);
|
||||
BinaryWriter w = new BinaryWriter(stream);
|
||||
w.Write(e.Xfer.AssetData);
|
||||
w.Close();
|
||||
|
||||
// tell the application we've gotten the file
|
||||
xferTimeout.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class UploadRawTerrainCommand : Command
|
||||
{
|
||||
System.Threading.AutoResetEvent WaitForUploadComplete = new System.Threading.AutoResetEvent(false);
|
||||
|
||||
public UploadRawTerrainCommand(TestClient testClient)
|
||||
{
|
||||
Name = "uploadterrain";
|
||||
Description = "Upload a raw terrain file to a simulator. usage: uploadterrain filename";
|
||||
Category = CommandCategory.Simulator;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
string fileName = String.Empty;
|
||||
|
||||
if (args.Length != 1)
|
||||
return "Usage: uploadterrain filename";
|
||||
|
||||
|
||||
fileName = args[0];
|
||||
|
||||
if (!System.IO.File.Exists(fileName))
|
||||
{
|
||||
return String.Format("File {0} Does not exist", fileName);
|
||||
}
|
||||
|
||||
// Setup callbacks for upload request reply and progress indicator
|
||||
// so we can detect when the upload is complete
|
||||
Client.Assets.UploadProgress += new EventHandler<AssetUploadEventArgs>(Assets_UploadProgress);
|
||||
byte[] fileData = File.ReadAllBytes(fileName);
|
||||
|
||||
Client.Estate.UploadTerrain(fileData, fileName);
|
||||
|
||||
// Wait for upload to complete. Upload request is fired in callback from first request
|
||||
if (!WaitForUploadComplete.WaitOne(120000, false))
|
||||
{
|
||||
Cleanup();
|
||||
return "Timeout waiting for terrain file upload";
|
||||
}
|
||||
else
|
||||
{
|
||||
Cleanup();
|
||||
return "Terrain raw file uploaded and applied";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister previously subscribed event handlers
|
||||
/// </summary>
|
||||
private void Cleanup()
|
||||
{
|
||||
Client.Assets.UploadProgress -= new EventHandler<AssetUploadEventArgs>(Assets_UploadProgress);
|
||||
}
|
||||
|
||||
|
||||
void Assets_UploadProgress(object sender, AssetUploadEventArgs e)
|
||||
{
|
||||
if (e.Upload.Transferred == e.Upload.Size)
|
||||
{
|
||||
WaitForUploadComplete.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Console.WriteLine("Progress: {0}/{1} {2}/{3} {4}", upload.XferID, upload.ID, upload.Transferred, upload.Size, upload.Success);
|
||||
Console.Write(".");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
// the Namespace used for all TestClient commands
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows a list of friends
|
||||
/// </summary>
|
||||
public class FriendsCommand : Command
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for FriendsCommand class
|
||||
/// </summary>
|
||||
/// <param name="testClient">A reference to the TestClient object</param>
|
||||
public FriendsCommand(TestClient testClient)
|
||||
{
|
||||
// The name of the command
|
||||
Name = "friends";
|
||||
// A short description of the command with usage instructions
|
||||
Description = "List avatar friends. Usage: friends";
|
||||
Category = CommandCategory.Friends;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of current friends
|
||||
/// </summary>
|
||||
/// <param name="args">optional testClient command arguments</param>
|
||||
/// <param name="fromAgentID">The <seealso cref="OpenMetaverse.UUID"/>
|
||||
/// of the agent making the request</param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// initialize a StringBuilder object used to return the results
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Only iterate the Friends dictionary if we actually have friends!
|
||||
if (Client.Friends.FriendList.Count > 0)
|
||||
{
|
||||
// iterate over the InternalDictionary using a delegate to populate
|
||||
// our StringBuilder output string
|
||||
sb.AppendFormat("has {0} friends:", Client.Friends.FriendList.Count).AppendLine();
|
||||
Client.Friends.FriendList.ForEach(delegate(FriendInfo friend)
|
||||
{
|
||||
// append the name of the friend to our output
|
||||
sb.AppendFormat("{0}, {1}", friend.UUID, friend.Name).AppendLine();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have no friends :(
|
||||
sb.AppendLine("No Friends");
|
||||
}
|
||||
|
||||
// return the result
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class MapFriendCommand : Command
|
||||
{
|
||||
ManualResetEvent WaitforFriend = new ManualResetEvent(false);
|
||||
|
||||
public MapFriendCommand(TestClient testClient)
|
||||
{
|
||||
Name = "mapfriend";
|
||||
Description = "Show a friends location. Usage: mapfriend UUID";
|
||||
Category = CommandCategory.Friends;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return Description;
|
||||
|
||||
UUID targetID;
|
||||
|
||||
if (!UUID.TryParse(args[0], out targetID))
|
||||
return Description;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
EventHandler<FriendFoundReplyEventArgs> del = delegate(object sender, FriendFoundReplyEventArgs e)
|
||||
{
|
||||
if (!e.RegionHandle.Equals(0))
|
||||
sb.AppendFormat("Found Friend {0} in {1} at {2}/{3}", e.AgentID, e.RegionHandle, e.Location.X, e.Location.Y);
|
||||
else
|
||||
sb.AppendFormat("Found Friend {0}, But they appear to be offline", e.AgentID);
|
||||
|
||||
WaitforFriend.Set();
|
||||
};
|
||||
|
||||
|
||||
|
||||
Client.Friends.FriendFoundReply += del;
|
||||
WaitforFriend.Reset();
|
||||
Client.Friends.MapFriend(targetID);
|
||||
if (!WaitforFriend.WaitOne(10000, false))
|
||||
{
|
||||
sb.AppendFormat("Timeout waiting for reply, Do you have mapping rights on {0}?", targetID);
|
||||
}
|
||||
Client.Friends.FriendFoundReply -= del;
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes Avatars currently active group
|
||||
/// </summary>
|
||||
public class ActivateGroupCommand : Command
|
||||
{
|
||||
private ManualResetEvent GroupsEvent = new ManualResetEvent(false);
|
||||
string activeGroup;
|
||||
|
||||
public ActivateGroupCommand(TestClient testClient)
|
||||
{
|
||||
Name = "activategroup";
|
||||
Description = "Set a group as active. Usage: activategroup GroupName";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return Description;
|
||||
|
||||
activeGroup = string.Empty;
|
||||
|
||||
string groupName = String.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
groupName += args[i] + " ";
|
||||
groupName = groupName.Trim();
|
||||
|
||||
UUID groupUUID = Client.GroupName2UUID(groupName);
|
||||
if (UUID.Zero != groupUUID) {
|
||||
EventHandler<PacketReceivedEventArgs> pcallback = AgentDataUpdateHandler;
|
||||
Client.Network.RegisterCallback(PacketType.AgentDataUpdate, pcallback);
|
||||
|
||||
Console.WriteLine("setting " + groupName + " as active group");
|
||||
Client.Groups.ActivateGroup(groupUUID);
|
||||
GroupsEvent.WaitOne(30000, false);
|
||||
|
||||
Client.Network.UnregisterCallback(PacketType.AgentDataUpdate, pcallback);
|
||||
GroupsEvent.Reset();
|
||||
|
||||
/* A.Biondi
|
||||
* TODO: Handle titles choosing.
|
||||
*/
|
||||
|
||||
if (String.IsNullOrEmpty(activeGroup))
|
||||
return Client.ToString() + " failed to activate the group " + groupName;
|
||||
|
||||
return "Active group is now " + activeGroup;
|
||||
}
|
||||
return Client.ToString() + " doesn't seem to be member of the group " + groupName;
|
||||
}
|
||||
|
||||
private void AgentDataUpdateHandler(object sender, PacketReceivedEventArgs e)
|
||||
{
|
||||
AgentDataUpdatePacket p = (AgentDataUpdatePacket)e.Packet;
|
||||
if (p.AgentData.AgentID == Client.Self.AgentID)
|
||||
{
|
||||
activeGroup = Utils.BytesToString(p.AgentData.GroupName) + " ( " + Utils.BytesToString(p.AgentData.GroupTitle) + " )";
|
||||
GroupsEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// dumps group members to console
|
||||
/// </summary>
|
||||
public class GroupMembersCommand : Command
|
||||
{
|
||||
private ManualResetEvent GroupsEvent = new ManualResetEvent(false);
|
||||
private string GroupName;
|
||||
private UUID GroupUUID;
|
||||
private UUID GroupRequestID;
|
||||
|
||||
public GroupMembersCommand(TestClient testClient)
|
||||
{
|
||||
Name = "groupmembers";
|
||||
Description = "Dump group members to console. Usage: groupmembers GroupName";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return Description;
|
||||
|
||||
GroupName = String.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
GroupName += args[i] + " ";
|
||||
GroupName = GroupName.Trim();
|
||||
|
||||
GroupUUID = Client.GroupName2UUID(GroupName);
|
||||
if (UUID.Zero != GroupUUID) {
|
||||
Client.Groups.GroupMembersReply += GroupMembersHandler;
|
||||
GroupRequestID = Client.Groups.RequestGroupMembers(GroupUUID);
|
||||
GroupsEvent.WaitOne(30000, false);
|
||||
GroupsEvent.Reset();
|
||||
Client.Groups.GroupMembersReply -= GroupMembersHandler;
|
||||
return Client.ToString() + " got group members";
|
||||
}
|
||||
return Client.ToString() + " doesn't seem to be member of the group " + GroupName;
|
||||
}
|
||||
|
||||
private void GroupMembersHandler(object sender, GroupMembersReplyEventArgs e)
|
||||
{
|
||||
if (e.RequestID == GroupRequestID) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
sb.AppendFormat("GroupMembers: RequestID {0}", e.RequestID).AppendLine();
|
||||
sb.AppendFormat("GroupMembers: GroupUUID {0}", GroupUUID).AppendLine();
|
||||
sb.AppendFormat("GroupMembers: GroupName {0}", GroupName).AppendLine();
|
||||
if (e.Members.Count > 0)
|
||||
foreach (KeyValuePair<UUID, GroupMember> member in e.Members)
|
||||
sb.AppendFormat("GroupMembers: MemberUUID {0}", member.Key.ToString()).AppendLine();
|
||||
sb.AppendFormat("GroupMembers: MemberCount {0}", e.Members.Count).AppendLine();
|
||||
Console.WriteLine(sb.ToString());
|
||||
GroupsEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// dumps group roles to console
|
||||
/// </summary>
|
||||
public class GroupRolesCommand : Command
|
||||
{
|
||||
private ManualResetEvent GroupsEvent = new ManualResetEvent(false);
|
||||
private string GroupName;
|
||||
private UUID GroupUUID;
|
||||
private UUID GroupRequestID;
|
||||
|
||||
public GroupRolesCommand(TestClient testClient)
|
||||
{
|
||||
Name = "grouproles";
|
||||
Description = "Dump group roles to console. Usage: grouproles GroupName";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return Description;
|
||||
|
||||
GroupName = String.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
GroupName += args[i] + " ";
|
||||
GroupName = GroupName.Trim();
|
||||
|
||||
GroupUUID = Client.GroupName2UUID(GroupName);
|
||||
if (UUID.Zero != GroupUUID) {
|
||||
Client.Groups.GroupRoleDataReply += Groups_GroupRoles;
|
||||
GroupRequestID = Client.Groups.RequestGroupRoles(GroupUUID);
|
||||
GroupsEvent.WaitOne(30000, false);
|
||||
GroupsEvent.Reset();
|
||||
Client.Groups.GroupRoleDataReply += Groups_GroupRoles;
|
||||
return Client.ToString() + " got group roles";
|
||||
}
|
||||
return Client.ToString() + " doesn't seem to have any roles in the group " + GroupName;
|
||||
}
|
||||
|
||||
void Groups_GroupRoles(object sender, GroupRolesDataReplyEventArgs e)
|
||||
{
|
||||
if (e.RequestID == GroupRequestID)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
sb.AppendFormat("GroupRole: RequestID {0}", e.RequestID).AppendLine();
|
||||
sb.AppendFormat("GroupRole: GroupUUID {0}", GroupUUID).AppendLine();
|
||||
sb.AppendFormat("GroupRole: GroupName {0}", GroupName).AppendLine();
|
||||
if (e.Roles.Count > 0)
|
||||
foreach (KeyValuePair<UUID, GroupRole> role in e.Roles)
|
||||
sb.AppendFormat("GroupRole: Role {0} {1}|{2}", role.Value.ID, role.Value.Name, role.Value.Title).AppendLine();
|
||||
sb.AppendFormat("GroupRole: RoleCount {0}", e.Roles.Count).AppendLine();
|
||||
Console.WriteLine(sb.ToString());
|
||||
GroupsEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GroupsCommand : Command
|
||||
{
|
||||
public GroupsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "groups";
|
||||
Description = "List avatar groups. Usage: groups";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.ReloadGroupsCache();
|
||||
return getGroupsString();
|
||||
}
|
||||
|
||||
string getGroupsString()
|
||||
{
|
||||
if (null == Client.GroupsCache)
|
||||
return "Groups cache failed.";
|
||||
if (0 == Client.GroupsCache.Count)
|
||||
return "No groups";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("got "+Client.GroupsCache.Count +" groups:");
|
||||
foreach (Group group in Client.GroupsCache.Values)
|
||||
{
|
||||
sb.AppendLine(group.ID + ", " + group.Name);
|
||||
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class InviteGroupCommand : Command
|
||||
{
|
||||
public InviteGroupCommand(TestClient testClient)
|
||||
{
|
||||
Name = "invitegroup";
|
||||
Description = "invite an avatar into a group. Usage: invitegroup AvatarUUID GroupUUID RoleUUID*";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
return Description;
|
||||
|
||||
UUID avatar = UUID.Zero;
|
||||
UUID group = UUID.Zero;
|
||||
UUID role = UUID.Zero;
|
||||
List<UUID> roles = new List<UUID>();
|
||||
|
||||
if (!UUID.TryParse(args[0], out avatar))
|
||||
return "parse error avatar UUID";
|
||||
if (!UUID.TryParse(args[1], out group))
|
||||
return "parse error group UUID";
|
||||
if (2 == args.Length)
|
||||
roles.Add(UUID.Zero);
|
||||
else
|
||||
for (int i = 2; i < args.Length; i++)
|
||||
if (UUID.TryParse(args[i], out role))
|
||||
roles.Add(role);
|
||||
|
||||
Client.Groups.Invite(group, roles, avatar);
|
||||
|
||||
return "invited "+avatar+" to "+group;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class JoinGroupCommand : Command
|
||||
{
|
||||
ManualResetEvent GetGroupsSearchEvent = new ManualResetEvent(false);
|
||||
private UUID queryID = UUID.Zero;
|
||||
private UUID resolvedGroupID;
|
||||
private string groupName;
|
||||
private string resolvedGroupName;
|
||||
private bool joinedGroup;
|
||||
|
||||
public JoinGroupCommand(TestClient testClient)
|
||||
{
|
||||
Name = "joingroup";
|
||||
Description = "join a group. Usage: joingroup GroupName | joingroup UUID GroupId";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return Description;
|
||||
|
||||
groupName = String.Empty;
|
||||
resolvedGroupID = UUID.Zero;
|
||||
resolvedGroupName = String.Empty;
|
||||
|
||||
if (args[0].ToLower() == "uuid")
|
||||
{
|
||||
if (args.Length < 2)
|
||||
return Description;
|
||||
|
||||
if (!UUID.TryParse((resolvedGroupName = groupName = args[1]), out resolvedGroupID))
|
||||
return resolvedGroupName + " doesn't seem a valid UUID";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
groupName += args[i] + " ";
|
||||
groupName = groupName.Trim();
|
||||
|
||||
Client.Directory.DirGroupsReply += Directory_DirGroups;
|
||||
|
||||
queryID = Client.Directory.StartGroupSearch(groupName, 0);
|
||||
|
||||
GetGroupsSearchEvent.WaitOne(60000, false);
|
||||
|
||||
Client.Directory.DirGroupsReply -= Directory_DirGroups;
|
||||
|
||||
GetGroupsSearchEvent.Reset();
|
||||
}
|
||||
|
||||
if (resolvedGroupID == UUID.Zero)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resolvedGroupName))
|
||||
return "Unable to obtain UUID for group " + groupName;
|
||||
else
|
||||
return resolvedGroupName;
|
||||
}
|
||||
|
||||
Client.Groups.GroupJoinedReply += Groups_OnGroupJoined;
|
||||
Client.Groups.RequestJoinGroup(resolvedGroupID);
|
||||
|
||||
/* A.Biondi
|
||||
* TODO: implement the pay to join procedure.
|
||||
*/
|
||||
|
||||
GetGroupsSearchEvent.WaitOne(60000, false);
|
||||
|
||||
Client.Groups.GroupJoinedReply -= Groups_GroupJoined;
|
||||
GetGroupsSearchEvent.Reset();
|
||||
Client.ReloadGroupsCache();
|
||||
|
||||
if (joinedGroup)
|
||||
return "Joined the group " + resolvedGroupName;
|
||||
return "Unable to join the group " + resolvedGroupName;
|
||||
}
|
||||
|
||||
void Groups_GroupJoined(object sender, GroupOperationEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void Directory_DirGroups(object sender, DirGroupsReplyEventArgs e)
|
||||
{
|
||||
if (queryID == e.QueryID)
|
||||
{
|
||||
queryID = UUID.Zero;
|
||||
if (e.MatchedGroups.Count < 1)
|
||||
{
|
||||
Console.WriteLine("ERROR: Got an empty reply");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e.MatchedGroups.Count > 1)
|
||||
{
|
||||
/* A.Biondi
|
||||
* The Group search doesn't work as someone could expect...
|
||||
* It'll give back to you a long list of groups even if the
|
||||
* searchText (groupName) matches esactly one of the groups
|
||||
* names present on the server, so we need to check each result.
|
||||
* UUIDs of the matching groups are written on the console.
|
||||
*/
|
||||
Console.WriteLine("Matching groups are:\n");
|
||||
foreach (DirectoryManager.GroupSearchData groupRetrieved in e.MatchedGroups)
|
||||
{
|
||||
Console.WriteLine(groupRetrieved.GroupName + "\t\t\t(" +
|
||||
Name + " UUID " + groupRetrieved.GroupID.ToString() + ")");
|
||||
|
||||
if (groupRetrieved.GroupName.ToLower() == groupName.ToLower())
|
||||
{
|
||||
resolvedGroupID = groupRetrieved.GroupID;
|
||||
resolvedGroupName = groupRetrieved.GroupName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(resolvedGroupName))
|
||||
resolvedGroupName = "Ambiguous name. Found " + e.MatchedGroups.Count.ToString() + " groups (UUIDs on console)";
|
||||
}
|
||||
|
||||
}
|
||||
GetGroupsSearchEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void Groups_OnGroupJoined(object sender, GroupOperationEventArgs e)
|
||||
{
|
||||
Console.WriteLine(Client.ToString() + (e.Success ? " joined " : " failed to join ") + e.GroupID.ToString());
|
||||
|
||||
/* A.Biondi
|
||||
* This code is not necessary because it is yet present in the
|
||||
* GroupCommand.cs as well. So the new group will be activated by
|
||||
* the mentioned command. If the GroupCommand.cs would change,
|
||||
* just uncomment the following two lines.
|
||||
|
||||
if (success)
|
||||
{
|
||||
Console.WriteLine(Client.ToString() + " setting " + groupID.ToString() + " as the active group");
|
||||
Client.Groups.ActivateGroup(groupID);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
joinedGroup = e.Success;
|
||||
GetGroupsSearchEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class LeaveGroupCommand : Command
|
||||
{
|
||||
ManualResetEvent GroupsEvent = new ManualResetEvent(false);
|
||||
private bool leftGroup;
|
||||
|
||||
public LeaveGroupCommand(TestClient testClient)
|
||||
{
|
||||
Name = "leavegroup";
|
||||
Description = "Leave a group. Usage: leavegroup GroupName";
|
||||
Category = CommandCategory.Groups;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return Description;
|
||||
|
||||
string groupName = String.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
groupName += args[i] + " ";
|
||||
groupName = groupName.Trim();
|
||||
|
||||
UUID groupUUID = Client.GroupName2UUID(groupName);
|
||||
if (UUID.Zero != groupUUID) {
|
||||
Client.Groups.GroupLeaveReply += Groups_GroupLeft;
|
||||
Client.Groups.LeaveGroup(groupUUID);
|
||||
|
||||
GroupsEvent.WaitOne(30000, false);
|
||||
Client.Groups.GroupLeaveReply -= Groups_GroupLeft;
|
||||
|
||||
GroupsEvent.Reset();
|
||||
Client.ReloadGroupsCache();
|
||||
|
||||
if (leftGroup)
|
||||
return Client.ToString() + " has left the group " + groupName;
|
||||
return "failed to leave the group " + groupName;
|
||||
}
|
||||
return Client.ToString() + " doesn't seem to be member of the group " + groupName;
|
||||
}
|
||||
|
||||
void Groups_GroupLeft(object sender, GroupOperationEventArgs e)
|
||||
{
|
||||
Console.WriteLine(Client.ToString() + (e.Success ? " has left group " : " failed to left group ") + e.GroupID.ToString());
|
||||
|
||||
leftGroup = e.Success;
|
||||
GroupsEvent.Set();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.ComponentModel;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.TestClient;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class QueuedDownloadInfo
|
||||
{
|
||||
public UUID AssetID;
|
||||
public UUID ItemID;
|
||||
public UUID TaskID;
|
||||
public UUID OwnerID;
|
||||
public AssetType Type;
|
||||
public string FileName;
|
||||
public DateTime WhenRequested;
|
||||
public bool IsRequested;
|
||||
|
||||
public QueuedDownloadInfo(string file, UUID asset, UUID item, UUID task, UUID owner, AssetType type)
|
||||
{
|
||||
FileName = file;
|
||||
AssetID = asset;
|
||||
ItemID = item;
|
||||
TaskID = task;
|
||||
OwnerID = owner;
|
||||
Type = type;
|
||||
WhenRequested = DateTime.Now;
|
||||
IsRequested = false;
|
||||
}
|
||||
}
|
||||
|
||||
public class BackupCommand : Command
|
||||
{
|
||||
/// <summary>Maximum number of transfer requests to send to the server</summary>
|
||||
private const int MAX_TRANSFERS = 10;
|
||||
|
||||
// all items here, fed by the inventory walking thread
|
||||
private Queue<QueuedDownloadInfo> PendingDownloads = new Queue<QueuedDownloadInfo>();
|
||||
|
||||
// items sent to the server here
|
||||
private List<QueuedDownloadInfo> CurrentDownloads = new List<QueuedDownloadInfo>(MAX_TRANSFERS);
|
||||
|
||||
// background workers
|
||||
private BackgroundWorker BackupWorker;
|
||||
private BackgroundWorker QueueWorker;
|
||||
|
||||
// some stats
|
||||
private int TextItemsFound;
|
||||
private int TextItemsTransferred;
|
||||
private int TextItemErrors;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// true if either of the background threads is running
|
||||
/// </summary>
|
||||
private bool BackgroundBackupRunning
|
||||
{
|
||||
get { return InventoryWalkerRunning || QueueRunnerRunning; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if the thread walking inventory is running
|
||||
/// </summary>
|
||||
private bool InventoryWalkerRunning
|
||||
{
|
||||
get { return BackupWorker != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if the thread feeding the queue to the server is running
|
||||
/// </summary>
|
||||
private bool QueueRunnerRunning
|
||||
{
|
||||
get { return QueueWorker != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a string summarizing activity
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string BackgroundBackupStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sbResult = new StringBuilder();
|
||||
sbResult.AppendFormat("{0} is {1} running.", Name, BoolToNot(BackgroundBackupRunning));
|
||||
if (TextItemErrors != 0 || TextItemsFound != 0 || TextItemsTransferred != 0)
|
||||
{
|
||||
sbResult.AppendFormat("\r\n{0} : Inventory walker ( {1} running ) has found {2} items.",
|
||||
Name, BoolToNot(InventoryWalkerRunning), TextItemsFound);
|
||||
sbResult.AppendFormat("\r\n{0} : Server Transfers ( {1} running ) has transferred {2} items with {3} errors.",
|
||||
Name, BoolToNot(QueueRunnerRunning), TextItemsTransferred, TextItemErrors);
|
||||
sbResult.AppendFormat("\r\n{0} : {1} items in Queue, {2} items requested from server.",
|
||||
Name, PendingDownloads.Count, CurrentDownloads.Count);
|
||||
}
|
||||
return sbResult.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
|
||||
public BackupCommand(TestClient testClient)
|
||||
{
|
||||
Name = "backuptext";
|
||||
Description = "Backup inventory to a folder on your hard drive. Usage: " + Name + " [to <directory>] | [abort] | [status]";
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
|
||||
if (args.Length == 1 && args[0] == "status")
|
||||
{
|
||||
return BackgroundBackupStatus;
|
||||
}
|
||||
else if (args.Length == 1 && args[0] == "abort")
|
||||
{
|
||||
if (!BackgroundBackupRunning)
|
||||
return BackgroundBackupStatus;
|
||||
|
||||
BackupWorker.CancelAsync();
|
||||
QueueWorker.CancelAsync();
|
||||
|
||||
Thread.Sleep(500);
|
||||
|
||||
// check status
|
||||
return BackgroundBackupStatus;
|
||||
}
|
||||
else if (args.Length != 2)
|
||||
{
|
||||
return "Usage: " + Name + " [to <directory>] | [abort] | [status]";
|
||||
}
|
||||
else if (BackgroundBackupRunning)
|
||||
{
|
||||
return BackgroundBackupStatus;
|
||||
}
|
||||
|
||||
QueueWorker = new BackgroundWorker();
|
||||
QueueWorker.WorkerSupportsCancellation = true;
|
||||
QueueWorker.DoWork += new DoWorkEventHandler(bwQueueRunner_DoWork);
|
||||
QueueWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwQueueRunner_RunWorkerCompleted);
|
||||
|
||||
QueueWorker.RunWorkerAsync();
|
||||
|
||||
BackupWorker = new BackgroundWorker();
|
||||
BackupWorker.WorkerSupportsCancellation = true;
|
||||
BackupWorker.DoWork += new DoWorkEventHandler(bwBackup_DoWork);
|
||||
BackupWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwBackup_RunWorkerCompleted);
|
||||
|
||||
BackupWorker.RunWorkerAsync(args);
|
||||
return "Started background operations.";
|
||||
}
|
||||
|
||||
void bwQueueRunner_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
QueueWorker = null;
|
||||
Console.WriteLine(BackgroundBackupStatus);
|
||||
}
|
||||
|
||||
void bwQueueRunner_DoWork(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
TextItemErrors = TextItemsTransferred = 0;
|
||||
|
||||
while (QueueWorker.CancellationPending == false)
|
||||
{
|
||||
// have any timed out?
|
||||
if (CurrentDownloads.Count > 0)
|
||||
{
|
||||
foreach (QueuedDownloadInfo qdi in CurrentDownloads)
|
||||
{
|
||||
if ((qdi.WhenRequested + TimeSpan.FromSeconds(60)) < DateTime.Now)
|
||||
{
|
||||
Logger.DebugLog(Name + ": timeout on asset " + qdi.AssetID.ToString(), Client);
|
||||
// submit request again
|
||||
Client.Assets.RequestInventoryAsset(
|
||||
qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true, Assets_OnAssetReceived);
|
||||
qdi.WhenRequested = DateTime.Now;
|
||||
qdi.IsRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PendingDownloads.Count != 0)
|
||||
{
|
||||
// room in the server queue?
|
||||
if (CurrentDownloads.Count < MAX_TRANSFERS)
|
||||
{
|
||||
// yes
|
||||
QueuedDownloadInfo qdi = PendingDownloads.Dequeue();
|
||||
qdi.WhenRequested = DateTime.Now;
|
||||
qdi.IsRequested = true;
|
||||
Client.Assets.RequestInventoryAsset(
|
||||
qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true, Assets_OnAssetReceived);
|
||||
|
||||
lock (CurrentDownloads) CurrentDownloads.Add(qdi);
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentDownloads.Count == 0 && PendingDownloads.Count == 0 && BackupWorker == null)
|
||||
{
|
||||
Logger.DebugLog(Name + ": both transfer queues empty AND inventory walking thread is done", Client);
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
void bwBackup_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
Console.WriteLine(Name + ": Inventory walking thread done.");
|
||||
BackupWorker = null;
|
||||
}
|
||||
|
||||
private void bwBackup_DoWork(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
string[] args;
|
||||
|
||||
TextItemsFound = 0;
|
||||
|
||||
args = (string[])e.Argument;
|
||||
|
||||
lock (CurrentDownloads) CurrentDownloads.Clear();
|
||||
|
||||
// FIXME:
|
||||
//Client.Inventory.RequestFolderContents(Client.Inventory.Store.RootFolder.UUID, Client.Self.AgentID,
|
||||
// true, true, false, InventorySortOrder.ByName);
|
||||
|
||||
DirectoryInfo di = new DirectoryInfo(args[1]);
|
||||
|
||||
// recurse on the root folder into the entire inventory
|
||||
BackupFolder(Client.Inventory.Store.RootNode, di.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BackupFolder - recurse through the inventory nodes sending scripts and notecards to the transfer queue
|
||||
/// </summary>
|
||||
/// <param name="folder">The current leaf in the inventory tree</param>
|
||||
/// <param name="sPathSoFar">path so far, in the form @"c:\here" -- this needs to be "clean" for the current filesystem</param>
|
||||
private void BackupFolder(InventoryNode folder, string sPathSoFar)
|
||||
{
|
||||
|
||||
// FIXME:
|
||||
//Client.Inventory.RequestFolderContents(folder.Data.UUID, Client.Self.AgentID, true, true, false,
|
||||
// InventorySortOrder.ByName);
|
||||
|
||||
// first scan this folder for text
|
||||
foreach (InventoryNode iNode in folder.Nodes.Values)
|
||||
{
|
||||
if (BackupWorker.CancellationPending)
|
||||
return;
|
||||
if (iNode.Data is OpenMetaverse.InventoryItem)
|
||||
{
|
||||
InventoryItem ii = iNode.Data as InventoryItem;
|
||||
if (ii.AssetType == AssetType.LSLText || ii.AssetType == AssetType.Notecard)
|
||||
{
|
||||
// check permissions on scripts
|
||||
if (ii.AssetType == AssetType.LSLText)
|
||||
{
|
||||
if ((ii.Permissions.OwnerMask & PermissionMask.Modify) == PermissionMask.None)
|
||||
{
|
||||
// skip this one
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
string sExtension = (ii.AssetType == AssetType.LSLText) ? ".lsl" : ".txt";
|
||||
// make the output file
|
||||
string sPath = sPathSoFar + @"\" + MakeValid(ii.Name.Trim()) + sExtension;
|
||||
|
||||
// create the new qdi
|
||||
QueuedDownloadInfo qdi = new QueuedDownloadInfo(sPath, ii.AssetUUID, iNode.Data.UUID, UUID.Zero,
|
||||
Client.Self.AgentID, ii.AssetType);
|
||||
|
||||
// add it to the queue
|
||||
lock (PendingDownloads)
|
||||
{
|
||||
TextItemsFound++;
|
||||
PendingDownloads.Enqueue(qdi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now run any subfolders
|
||||
foreach (InventoryNode i in folder.Nodes.Values)
|
||||
{
|
||||
if (BackupWorker.CancellationPending)
|
||||
return;
|
||||
else if (i.Data is OpenMetaverse.InventoryFolder)
|
||||
BackupFolder(i, sPathSoFar + @"\" + MakeValid(i.Data.Name.Trim()));
|
||||
}
|
||||
}
|
||||
|
||||
private string MakeValid(string path)
|
||||
{
|
||||
// FIXME: We need to strip illegal characters out
|
||||
return path.Trim().Replace('"', '\'');
|
||||
}
|
||||
|
||||
private void Assets_OnAssetReceived(AssetDownload asset, Asset blah)
|
||||
{
|
||||
lock (CurrentDownloads)
|
||||
{
|
||||
// see if we have this in our transfer list
|
||||
QueuedDownloadInfo r = CurrentDownloads.Find(delegate(QueuedDownloadInfo q)
|
||||
{
|
||||
return q.AssetID == asset.AssetID;
|
||||
});
|
||||
|
||||
if (r != null && r.AssetID == asset.AssetID)
|
||||
{
|
||||
if (asset.Success)
|
||||
{
|
||||
// create the directory to put this in
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(r.FileName));
|
||||
|
||||
// write out the file
|
||||
File.WriteAllBytes(r.FileName, asset.AssetData);
|
||||
Logger.DebugLog(Name + " Wrote: " + r.FileName, Client);
|
||||
TextItemsTransferred++;
|
||||
}
|
||||
else
|
||||
{
|
||||
TextItemErrors++;
|
||||
Console.WriteLine("{0}: Download of asset {1} ({2}) failed with status {3}", Name, r.FileName,
|
||||
r.AssetID.ToString(), asset.Status.ToString());
|
||||
}
|
||||
|
||||
// remove the entry
|
||||
CurrentDownloads.Remove(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns blank or "not" if false
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
private static string BoolToNot(bool b)
|
||||
{
|
||||
return b ? String.Empty : "not";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class BalanceCommand: Command
|
||||
{
|
||||
public BalanceCommand(TestClient testClient)
|
||||
{
|
||||
Name = "balance";
|
||||
Description = "Shows the amount of L$.";
|
||||
Category = CommandCategory.Other;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
System.Threading.AutoResetEvent waitBalance = new System.Threading.AutoResetEvent(false);
|
||||
|
||||
EventHandler<BalanceEventArgs> del = delegate(object sender, BalanceEventArgs e) { waitBalance.Set(); };
|
||||
Client.Self.MoneyBalance += del;
|
||||
Client.Self.RequestBalance();
|
||||
String result = "Timeout waiting for balance reply";
|
||||
if (waitBalance.WaitOne(10000, false))
|
||||
{
|
||||
result = Client.ToString() + " has L$: " + Client.Self.Balance;
|
||||
}
|
||||
Client.Self.MoneyBalance -= del;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Inventory.Shell
|
||||
{
|
||||
public class ChangeDirectoryCommand : Command
|
||||
{
|
||||
private OpenMetaverse.Inventory Inventory;
|
||||
|
||||
public ChangeDirectoryCommand(TestClient client)
|
||||
{
|
||||
Name = "cd";
|
||||
Description = "Changes the current working inventory folder.";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Inventory = Client.Inventory.Store;
|
||||
|
||||
if (args.Length > 1)
|
||||
return "Usage: cd [path-to-folder]";
|
||||
string pathStr = "";
|
||||
string[] path = null;
|
||||
if (args.Length == 0)
|
||||
{
|
||||
path = new string[] { "" };
|
||||
// cd without any arguments doesn't do anything.
|
||||
}
|
||||
else if (args.Length == 1)
|
||||
{
|
||||
pathStr = args[0];
|
||||
path = pathStr.Split(new char[] { '/' });
|
||||
// Use '/' as a path seperator.
|
||||
}
|
||||
InventoryFolder currentFolder = Client.CurrentDirectory;
|
||||
if (pathStr.StartsWith("/"))
|
||||
currentFolder = Inventory.RootFolder;
|
||||
|
||||
if (currentFolder == null) // We need this to be set to something.
|
||||
return "Error: Client not logged in.";
|
||||
|
||||
// Traverse the path, looking for the
|
||||
for (int i = 0; i < path.Length; ++i)
|
||||
{
|
||||
string nextName = path[i];
|
||||
if (string.IsNullOrEmpty(nextName) || nextName == ".")
|
||||
continue; // Ignore '.' and blanks, stay in the current directory.
|
||||
if (nextName == ".." && currentFolder != Inventory.RootFolder)
|
||||
{
|
||||
// If we encounter .., move to the parent folder.
|
||||
currentFolder = Inventory[currentFolder.ParentUUID] as InventoryFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<InventoryBase> currentContents = Inventory.GetContents(currentFolder);
|
||||
// Try and find an InventoryBase with the corresponding name.
|
||||
bool found = false;
|
||||
foreach (InventoryBase item in currentContents)
|
||||
{
|
||||
// Allow lookup by UUID as well as name:
|
||||
if (item.Name == nextName || item.UUID.ToString() == nextName)
|
||||
{
|
||||
found = true;
|
||||
if (item is InventoryFolder)
|
||||
{
|
||||
currentFolder = item as InventoryFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return item.Name + " is not a folder.";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return nextName + " not found in " + currentFolder.Name;
|
||||
}
|
||||
}
|
||||
Client.CurrentDirectory = currentFolder;
|
||||
return "Current folder: " + currentFolder.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class CreateNotecardCommand : Command
|
||||
{
|
||||
const int NOTECARD_CREATE_TIMEOUT = 1000 * 10;
|
||||
const int NOTECARD_FETCH_TIMEOUT = 1000 * 10;
|
||||
const int INVENTORY_FETCH_TIMEOUT = 1000 * 10;
|
||||
|
||||
public CreateNotecardCommand(TestClient testClient)
|
||||
{
|
||||
Name = "createnotecard";
|
||||
Description = "Creates a notecard from a local text file and optionally embed an inventory item. Usage: createnotecard filename.txt [itemid]";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
UUID embedItemID = UUID.Zero, notecardItemID = UUID.Zero, notecardAssetID = UUID.Zero;
|
||||
string filename, fileData;
|
||||
bool success = false, finalUploadSuccess = false;
|
||||
string message = String.Empty;
|
||||
AutoResetEvent notecardEvent = new AutoResetEvent(false);
|
||||
|
||||
if (args.Length == 1)
|
||||
{
|
||||
filename = args[0];
|
||||
}
|
||||
else if (args.Length == 2)
|
||||
{
|
||||
filename = args[0];
|
||||
UUID.TryParse(args[1], out embedItemID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Usage: createnotecard filename.txt";
|
||||
}
|
||||
|
||||
if (!File.Exists(filename))
|
||||
return "File \"" + filename + "\" does not exist";
|
||||
|
||||
try { fileData = File.ReadAllText(filename); }
|
||||
catch (Exception ex) { return "Failed to open " + filename + ": " + ex.Message; }
|
||||
|
||||
#region Notecard asset data
|
||||
|
||||
AssetNotecard notecard = new AssetNotecard();
|
||||
notecard.BodyText = fileData;
|
||||
|
||||
// Item embedding
|
||||
if (embedItemID != UUID.Zero)
|
||||
{
|
||||
// Try to fetch the inventory item
|
||||
InventoryItem item = FetchItem(embedItemID);
|
||||
if (item != null)
|
||||
{
|
||||
notecard.EmbeddedItems = new List<InventoryItem> { item };
|
||||
notecard.BodyText += (char)0xdbc0 + (char)0xdc00;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to fetch inventory item " + embedItemID;
|
||||
}
|
||||
}
|
||||
|
||||
notecard.Encode();
|
||||
|
||||
#endregion Notecard asset data
|
||||
|
||||
Client.Inventory.RequestCreateItem(Client.Inventory.FindFolderForType(AssetType.Notecard),
|
||||
filename, filename + " created by OpenMetaverse TestClient " + DateTime.Now, AssetType.Notecard,
|
||||
UUID.Random(), InventoryType.Notecard, PermissionMask.All,
|
||||
delegate(bool createSuccess, InventoryItem item)
|
||||
{
|
||||
if (createSuccess)
|
||||
{
|
||||
#region Upload an empty notecard asset first
|
||||
|
||||
AutoResetEvent emptyNoteEvent = new AutoResetEvent(false);
|
||||
AssetNotecard empty = new AssetNotecard();
|
||||
empty.BodyText = "\n";
|
||||
empty.Encode();
|
||||
|
||||
Client.Inventory.RequestUploadNotecardAsset(empty.AssetData, item.UUID,
|
||||
delegate(bool uploadSuccess, string status, UUID itemID, UUID assetID)
|
||||
{
|
||||
notecardItemID = itemID;
|
||||
notecardAssetID = assetID;
|
||||
success = uploadSuccess;
|
||||
message = status ?? "Unknown error uploading notecard asset";
|
||||
emptyNoteEvent.Set();
|
||||
});
|
||||
|
||||
emptyNoteEvent.WaitOne(NOTECARD_CREATE_TIMEOUT, false);
|
||||
|
||||
#endregion Upload an empty notecard asset first
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Upload the actual notecard asset
|
||||
Client.Inventory.RequestUploadNotecardAsset(notecard.AssetData, item.UUID,
|
||||
delegate(bool uploadSuccess, string status, UUID itemID, UUID assetID)
|
||||
{
|
||||
notecardItemID = itemID;
|
||||
notecardAssetID = assetID;
|
||||
finalUploadSuccess = uploadSuccess;
|
||||
message = status ?? "Unknown error uploading notecard asset";
|
||||
notecardEvent.Set();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
notecardEvent.Set();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "Notecard item creation failed";
|
||||
notecardEvent.Set();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
notecardEvent.WaitOne(NOTECARD_CREATE_TIMEOUT, false);
|
||||
|
||||
if (finalUploadSuccess)
|
||||
{
|
||||
Logger.Log("Notecard successfully created, ItemID " + notecardItemID + " AssetID " + notecardAssetID, Helpers.LogLevel.Info);
|
||||
return DownloadNotecard(notecardItemID, notecardAssetID);
|
||||
}
|
||||
else
|
||||
return "Notecard creation failed: " + message;
|
||||
}
|
||||
|
||||
private InventoryItem FetchItem(UUID itemID)
|
||||
{
|
||||
InventoryItem fetchItem = null;
|
||||
AutoResetEvent fetchItemEvent = new AutoResetEvent(false);
|
||||
|
||||
EventHandler<ItemReceivedEventArgs> itemReceivedCallback =
|
||||
delegate(object sender, ItemReceivedEventArgs e)
|
||||
{
|
||||
if (e.Item.UUID == itemID)
|
||||
{
|
||||
fetchItem = e.Item;
|
||||
fetchItemEvent.Set();
|
||||
}
|
||||
};
|
||||
|
||||
Client.Inventory.ItemReceived += itemReceivedCallback;
|
||||
|
||||
Client.Inventory.RequestFetchInventory(itemID, Client.Self.AgentID);
|
||||
|
||||
fetchItemEvent.WaitOne(INVENTORY_FETCH_TIMEOUT, false);
|
||||
|
||||
Client.Inventory.ItemReceived -= itemReceivedCallback;
|
||||
|
||||
return fetchItem;
|
||||
}
|
||||
|
||||
private string DownloadNotecard(UUID itemID, UUID assetID)
|
||||
{
|
||||
AutoResetEvent assetDownloadEvent = new AutoResetEvent(false);
|
||||
byte[] notecardData = null;
|
||||
string error = "Timeout";
|
||||
|
||||
Client.Assets.RequestInventoryAsset(assetID, itemID, UUID.Zero, Client.Self.AgentID, AssetType.Notecard, true,
|
||||
delegate(AssetDownload transfer, Asset asset)
|
||||
{
|
||||
if (transfer.Success)
|
||||
notecardData = transfer.AssetData;
|
||||
else
|
||||
error = transfer.Status.ToString();
|
||||
assetDownloadEvent.Set();
|
||||
}
|
||||
);
|
||||
|
||||
assetDownloadEvent.WaitOne(NOTECARD_FETCH_TIMEOUT, false);
|
||||
|
||||
if (notecardData != null)
|
||||
return Encoding.UTF8.GetString(notecardData);
|
||||
else
|
||||
return "Error downloading notecard asset: " + error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Inventory Example, Moves a folder to the Trash folder
|
||||
/// </summary>
|
||||
public class DeleteFolderCommand : Command
|
||||
{
|
||||
public DeleteFolderCommand(TestClient testClient)
|
||||
{
|
||||
Name = "deleteFolder";
|
||||
Description = "Moves a folder to the Trash Folder";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// parse the command line
|
||||
string target = String.Empty;
|
||||
for (int ct = 0; ct < args.Length; ct++)
|
||||
target = target + args[ct] + " ";
|
||||
target = target.TrimEnd();
|
||||
|
||||
// initialize results list
|
||||
List<InventoryBase> found = new List<InventoryBase>();
|
||||
|
||||
// find the folder
|
||||
found = Client.Inventory.LocalFind(Client.Inventory.Store.RootFolder.UUID, target.Split('/'), 0, true);
|
||||
|
||||
if (found.Count.Equals(1))
|
||||
{
|
||||
// move the folder to the trash folder
|
||||
Client.Inventory.MoveFolder(found[0].UUID, Client.Inventory.FindFolderForType(AssetType.TrashFolder));
|
||||
|
||||
return String.Format("Moved folder {0} to Trash", found[0].Name);
|
||||
}
|
||||
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class DownloadCommand : Command
|
||||
{
|
||||
UUID AssetID;
|
||||
AssetType assetType;
|
||||
AutoResetEvent DownloadHandle = new AutoResetEvent(false);
|
||||
bool Success;
|
||||
string usage = "Usage: download [uuid] [assetType]";
|
||||
|
||||
public DownloadCommand(TestClient testClient)
|
||||
{
|
||||
Name = "download";
|
||||
Description = "Downloads the specified asset. Usage: download [uuid] [assetType]";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
return usage;
|
||||
|
||||
Success = false;
|
||||
AssetID = UUID.Zero;
|
||||
assetType = AssetType.Unknown;
|
||||
DownloadHandle.Reset();
|
||||
|
||||
if (!UUID.TryParse(args[0], out AssetID))
|
||||
return usage;
|
||||
|
||||
try {
|
||||
assetType = (AssetType)Enum.Parse(typeof(AssetType), args[1], true);
|
||||
} catch (ArgumentException) {
|
||||
return usage;
|
||||
}
|
||||
if (!Enum.IsDefined(typeof(AssetType), assetType))
|
||||
return usage;
|
||||
|
||||
// Start the asset download
|
||||
Client.Assets.RequestAsset(AssetID, assetType, true, Assets_OnAssetReceived);
|
||||
|
||||
if (DownloadHandle.WaitOne(120 * 1000, false))
|
||||
{
|
||||
if (Success)
|
||||
return String.Format("Saved {0}.{1}", AssetID, assetType.ToString().ToLower());
|
||||
else
|
||||
return String.Format("Failed to download asset {0}, perhaps {1} is the incorrect asset type?",
|
||||
AssetID, assetType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Timed out waiting for texture download";
|
||||
}
|
||||
}
|
||||
|
||||
private void Assets_OnAssetReceived(AssetDownload transfer, Asset asset)
|
||||
{
|
||||
if (transfer.AssetID == AssetID)
|
||||
{
|
||||
if (transfer.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(String.Format("{0}.{1}", AssetID,
|
||||
assetType.ToString().ToLower()), asset.AssetData);
|
||||
Success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadHandle.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class DumpOutfitCommand : Command
|
||||
{
|
||||
List<UUID> OutfitAssets = new List<UUID>();
|
||||
|
||||
public DumpOutfitCommand(TestClient testClient)
|
||||
{
|
||||
Name = "dumpoutfit";
|
||||
Description = "Dumps all of the textures from an avatars outfit to the hard drive. Usage: dumpoutfit [avatar-uuid]";
|
||||
Category = CommandCategory.Inventory;
|
||||
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return "Usage: dumpoutfit [avatar-uuid]";
|
||||
|
||||
UUID target;
|
||||
|
||||
if (!UUID.TryParse(args[0], out target))
|
||||
return "Usage: dumpoutfit [avatar-uuid]";
|
||||
|
||||
lock (Client.Network.Simulators)
|
||||
{
|
||||
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||
{
|
||||
Avatar targetAv = Client.Network.Simulators[i].ObjectsAvatars.Find(
|
||||
delegate(Avatar avatar)
|
||||
{
|
||||
return avatar.ID == target;
|
||||
}
|
||||
);
|
||||
|
||||
if (targetAv != null)
|
||||
{
|
||||
StringBuilder output = new StringBuilder("Downloading ");
|
||||
|
||||
lock (OutfitAssets) OutfitAssets.Clear();
|
||||
|
||||
for (int j = 0; j < targetAv.Textures.FaceTextures.Length; j++)
|
||||
{
|
||||
Primitive.TextureEntryFace face = targetAv.Textures.FaceTextures[j];
|
||||
|
||||
if (face != null)
|
||||
{
|
||||
ImageType type = ImageType.Normal;
|
||||
|
||||
switch ((AvatarTextureIndex)j)
|
||||
{
|
||||
case AvatarTextureIndex.HeadBaked:
|
||||
case AvatarTextureIndex.EyesBaked:
|
||||
case AvatarTextureIndex.UpperBaked:
|
||||
case AvatarTextureIndex.LowerBaked:
|
||||
case AvatarTextureIndex.SkirtBaked:
|
||||
type = ImageType.Baked;
|
||||
break;
|
||||
}
|
||||
|
||||
OutfitAssets.Add(face.TextureID);
|
||||
Client.Assets.RequestImage(face.TextureID, type, Assets_OnImageReceived);
|
||||
output.Append(((AvatarTextureIndex)j).ToString());
|
||||
output.Append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "Couldn't find avatar " + target.ToString();
|
||||
}
|
||||
|
||||
private void Assets_OnImageReceived(TextureRequestState state, AssetTexture assetTexture)
|
||||
{
|
||||
lock (OutfitAssets)
|
||||
{
|
||||
if (OutfitAssets.Contains(assetTexture.AssetID))
|
||||
{
|
||||
if (state == TextureRequestState.Finished)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(assetTexture.AssetID + ".jp2", assetTexture.AssetData);
|
||||
Console.WriteLine("Wrote JPEG2000 image " + assetTexture.AssetID + ".jp2");
|
||||
|
||||
ManagedImage imgData;
|
||||
OpenJPEG.DecodeToImage(assetTexture.AssetData, out imgData);
|
||||
byte[] tgaFile = imgData.ExportTGA();
|
||||
File.WriteAllBytes(assetTexture.AssetID + ".tga", tgaFile);
|
||||
Console.WriteLine("Wrote TGA image " + assetTexture.AssetID + ".tga");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to download image " + assetTexture.AssetID);
|
||||
}
|
||||
|
||||
OutfitAssets.Remove(assetTexture.AssetID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class EmptyLostAndCommand : Command
|
||||
{
|
||||
/// <summary>
|
||||
/// TestClient command to download and display a notecard asset
|
||||
/// </summary>
|
||||
/// <param name="testClient"></param>
|
||||
public EmptyLostAndCommand(TestClient testClient)
|
||||
{
|
||||
Name = "emptylostandfound";
|
||||
Description = "Empty inventory Lost And Found folder";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exectute the command
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="fromAgentID"></param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Inventory.EmptyLostAndFound();
|
||||
return "Lost And Found Emptied";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class EmptyTrashCommand : Command
|
||||
{
|
||||
/// <summary>
|
||||
/// TestClient command to download and display a notecard asset
|
||||
/// </summary>
|
||||
/// <param name="testClient"></param>
|
||||
public EmptyTrashCommand(TestClient testClient)
|
||||
{
|
||||
Name = "emptytrash";
|
||||
Description = "Empty inventory Trash folder";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exectute the command
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="fromAgentID"></param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Inventory.EmptyTrash();
|
||||
return "Trash Emptied";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GiveAllCommand: Command
|
||||
{
|
||||
public GiveAllCommand(TestClient testClient)
|
||||
{
|
||||
Name = "giveAll";
|
||||
Description = "Gives you all it's money.";
|
||||
Category = CommandCategory.Other;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (fromAgentID == UUID.Zero)
|
||||
return "Unable to send money to console. This command only works when IMed.";
|
||||
|
||||
int amount = Client.Self.Balance;
|
||||
Client.Self.GiveAvatarMoney(fromAgentID, Client.Self.Balance, "TestClient.GiveAll");
|
||||
return "Gave $" + amount + " to " + fromAgentID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Inventory.Shell
|
||||
{
|
||||
class GiveItemCommand : Command
|
||||
{
|
||||
private InventoryManager Manager;
|
||||
private OpenMetaverse.Inventory Inventory;
|
||||
public GiveItemCommand(TestClient client)
|
||||
{
|
||||
Name = "give";
|
||||
Description = "Gives items from the current working directory to an avatar.";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
return "Usage: give <agent uuid> itemname";
|
||||
}
|
||||
UUID dest;
|
||||
if (!UUID.TryParse(args[0], out dest))
|
||||
{
|
||||
return "First argument expected agent UUID.";
|
||||
}
|
||||
Manager = Client.Inventory;
|
||||
Inventory = Manager.Store;
|
||||
string ret = "";
|
||||
string nl = "\n";
|
||||
|
||||
string target = String.Empty;
|
||||
for (int ct = 0; ct < args.Length; ct++)
|
||||
target = target + args[ct] + " ";
|
||||
target = target.TrimEnd();
|
||||
|
||||
string inventoryName = target;
|
||||
// WARNING: Uses local copy of inventory contents, need to download them first.
|
||||
List<InventoryBase> contents = Inventory.GetContents(Client.CurrentDirectory);
|
||||
bool found = false;
|
||||
foreach (InventoryBase b in contents)
|
||||
{
|
||||
if (inventoryName == b.Name || inventoryName == b.UUID.ToString())
|
||||
{
|
||||
found = true;
|
||||
if (b is InventoryItem)
|
||||
{
|
||||
InventoryItem item = b as InventoryItem;
|
||||
Manager.GiveItem(item.UUID, item.Name, item.AssetType, dest, true);
|
||||
ret += "Gave " + item.Name + " (" + item.AssetType + ")" + nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += "Unable to give folder " + b.Name + nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
ret += "No inventory item named " + inventoryName + " found." + nl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class InventoryCommand : Command
|
||||
{
|
||||
private Inventory Inventory;
|
||||
private InventoryManager Manager;
|
||||
|
||||
public InventoryCommand(TestClient testClient)
|
||||
{
|
||||
Name = "i";
|
||||
Description = "Prints out inventory.";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Manager = Client.Inventory;
|
||||
Inventory = Manager.Store;
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
InventoryFolder rootFolder = Inventory.RootFolder;
|
||||
PrintFolder(rootFolder, result, 0);
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
void PrintFolder(InventoryFolder f, StringBuilder result, int indent)
|
||||
{
|
||||
List<InventoryBase> contents = Manager.FolderContents(f.UUID, Client.Self.AgentID,
|
||||
true, true, InventorySortOrder.ByName, 3000);
|
||||
|
||||
if (contents != null)
|
||||
{
|
||||
foreach (InventoryBase i in contents)
|
||||
{
|
||||
result.AppendFormat("{0}{1} ({2})\n", new String(' ', indent * 2), i.Name, i.UUID);
|
||||
if (i is InventoryFolder)
|
||||
{
|
||||
InventoryFolder folder = (InventoryFolder)i;
|
||||
PrintFolder(folder, result, indent + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Inventory.Shell
|
||||
{
|
||||
public class ListContentsCommand : Command
|
||||
{
|
||||
private InventoryManager Manager;
|
||||
private OpenMetaverse.Inventory Inventory;
|
||||
public ListContentsCommand(TestClient client)
|
||||
{
|
||||
Name = "ls";
|
||||
Description = "Lists the contents of the current working inventory folder.";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length > 1)
|
||||
return "Usage: ls [-l]";
|
||||
bool longDisplay = false;
|
||||
if (args.Length > 0 && args[0] == "-l")
|
||||
longDisplay = true;
|
||||
|
||||
Manager = Client.Inventory;
|
||||
Inventory = Manager.Store;
|
||||
// WARNING: Uses local copy of inventory contents, need to download them first.
|
||||
List<InventoryBase> contents = Inventory.GetContents(Client.CurrentDirectory);
|
||||
string displayString = "";
|
||||
string nl = "\n"; // New line character
|
||||
// Pretty simple, just print out the contents.
|
||||
foreach (InventoryBase b in contents)
|
||||
{
|
||||
if (longDisplay)
|
||||
{
|
||||
// Generate a nicely formatted description of the item.
|
||||
// It kinda looks like the output of the unix ls.
|
||||
// starts with 'd' if the inventory is a folder, '-' if not.
|
||||
// 9 character permissions string
|
||||
// UUID of object
|
||||
// Name of object
|
||||
if (b is InventoryFolder)
|
||||
{
|
||||
InventoryFolder folder = b as InventoryFolder;
|
||||
displayString += "d--------- ";
|
||||
displayString += folder.UUID;
|
||||
displayString += " " + folder.Name;
|
||||
}
|
||||
else if (b is InventoryItem)
|
||||
{
|
||||
InventoryItem item = b as InventoryItem;
|
||||
displayString += "-";
|
||||
displayString += PermMaskString(item.Permissions.OwnerMask);
|
||||
displayString += PermMaskString(item.Permissions.GroupMask);
|
||||
displayString += PermMaskString(item.Permissions.EveryoneMask);
|
||||
displayString += " " + item.UUID;
|
||||
displayString += " " + item.Name;
|
||||
displayString += nl;
|
||||
displayString += " AssetID: " + item.AssetUUID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
displayString += b.Name;
|
||||
}
|
||||
displayString += nl;
|
||||
}
|
||||
return displayString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a 3-character summary of the PermissionMask
|
||||
/// CMT if the mask allows copy, mod and transfer
|
||||
/// -MT if it disallows copy
|
||||
/// --T if it only allows transfer
|
||||
/// --- if it disallows everything
|
||||
/// </summary>
|
||||
/// <param name="mask"></param>
|
||||
/// <returns></returns>
|
||||
private static string PermMaskString(PermissionMask mask)
|
||||
{
|
||||
string str = "";
|
||||
if (((uint)mask | (uint)PermissionMask.Copy) == (uint)PermissionMask.Copy)
|
||||
str += "C";
|
||||
else
|
||||
str += "-";
|
||||
if (((uint)mask | (uint)PermissionMask.Modify) == (uint)PermissionMask.Modify)
|
||||
str += "M";
|
||||
else
|
||||
str += "-";
|
||||
if (((uint)mask | (uint)PermissionMask.Transfer) == (uint)PermissionMask.Transfer)
|
||||
str += "T";
|
||||
else
|
||||
str += "-";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ObjectInventoryCommand : Command
|
||||
{
|
||||
public ObjectInventoryCommand(TestClient testClient)
|
||||
{
|
||||
Name = "objectinventory";
|
||||
Description = "Retrieves a listing of items inside an object (task inventory). Usage: objectinventory [objectID]";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return "Usage: objectinventory [objectID]";
|
||||
|
||||
uint objectLocalID;
|
||||
UUID objectID;
|
||||
if (!UUID.TryParse(args[0], out objectID))
|
||||
return "Usage: objectinventory [objectID]";
|
||||
|
||||
Primitive found = Client.Network.CurrentSim.ObjectsPrimitives.Find(delegate(Primitive prim) { return prim.ID == objectID; });
|
||||
if (found != null)
|
||||
objectLocalID = found.LocalID;
|
||||
else
|
||||
return "Couldn't find prim " + objectID.ToString();
|
||||
|
||||
List<InventoryBase> items = Client.Inventory.GetTaskInventory(objectID, objectLocalID, 1000 * 30);
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
string result = String.Empty;
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i] is InventoryFolder)
|
||||
{
|
||||
result += String.Format("[Folder] Name: {0}", items[i].Name) + Environment.NewLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
InventoryItem item = (InventoryItem)items[i];
|
||||
result += String.Format("[Item] Name: {0} Desc: {1} Type: {2}", item.Name, item.Description,
|
||||
item.AssetType) + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to download task inventory for " + objectLocalID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ScriptCommand : Command
|
||||
{
|
||||
public ScriptCommand(TestClient testClient)
|
||||
{
|
||||
Name = "script";
|
||||
Description = "Reads TestClient commands from a file. One command per line, arguments separated by spaces. Usage: script [filename]";
|
||||
Category = CommandCategory.TestClient;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return "Usage: script [filename]";
|
||||
|
||||
// Load the file
|
||||
string[] lines;
|
||||
try { lines = File.ReadAllLines(args[0]); }
|
||||
catch (Exception e) { return e.Message; }
|
||||
|
||||
// Execute all of the commands
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
string line = lines[i].Trim();
|
||||
|
||||
if (line.Length > 0)
|
||||
ClientManager.Instance.DoCommandAll(line, UUID.Zero);
|
||||
}
|
||||
|
||||
return "Finished executing " + lines.Length + " commands";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class TaskRunningCommand : Command
|
||||
{
|
||||
public TaskRunningCommand(TestClient testClient)
|
||||
{
|
||||
Name = "taskrunning";
|
||||
Description = "Retrieves or set IsRunning flag on items inside an object (task inventory). Usage: taskrunning objectID [[scriptName] true|false]";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return "Usage: taskrunning objectID [[scriptName] true|false]";
|
||||
|
||||
uint objectLocalID;
|
||||
UUID objectID;
|
||||
|
||||
if (!UUID.TryParse(args[0], out objectID))
|
||||
return "Usage: taskrunning objectID [[scriptName] true|false]";
|
||||
|
||||
Primitive found = Client.Network.CurrentSim.ObjectsPrimitives.Find(delegate(Primitive prim) { return prim.ID == objectID; });
|
||||
if (found != null)
|
||||
objectLocalID = found.LocalID;
|
||||
else
|
||||
return String.Format("Couldn't find prim {0}", objectID);
|
||||
|
||||
List<InventoryBase> items = Client.Inventory.GetTaskInventory(objectID, objectLocalID, 1000 * 30);
|
||||
|
||||
//bool wantSet = false;
|
||||
bool setTaskTo = false;
|
||||
if (items != null)
|
||||
{
|
||||
string result = String.Empty;
|
||||
string matching = String.Empty;
|
||||
bool setAny = false;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
matching = args[1];
|
||||
|
||||
string tf;
|
||||
if (args.Length > 2)
|
||||
{
|
||||
tf = args[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
tf = matching.ToLower();
|
||||
}
|
||||
if (tf == "true")
|
||||
{
|
||||
setAny = true;
|
||||
setTaskTo = true;
|
||||
}
|
||||
else if (tf == "false")
|
||||
{
|
||||
setAny = true;
|
||||
setTaskTo = false;
|
||||
}
|
||||
|
||||
}
|
||||
bool wasRunning = false;
|
||||
|
||||
EventHandler<ScriptRunningReplyEventArgs> callback;
|
||||
using (AutoResetEvent OnScriptRunningReset = new AutoResetEvent(false))
|
||||
{
|
||||
callback = ((object sender, ScriptRunningReplyEventArgs e) =>
|
||||
{
|
||||
if (e.ObjectID == objectID)
|
||||
{
|
||||
result += String.Format(" IsMono: {0} IsRunning: {1}", e.IsMono, e.IsRunning);
|
||||
wasRunning = e.IsRunning;
|
||||
OnScriptRunningReset.Set();
|
||||
}
|
||||
});
|
||||
|
||||
Client.Inventory.ScriptRunningReply += callback;
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i] is InventoryFolder)
|
||||
{
|
||||
// this shouldn't happen this year
|
||||
result += String.Format("[Folder] Name: {0}", items[i].Name) + Environment.NewLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
InventoryItem item = (InventoryItem)items[i];
|
||||
AssetType assetType = item.AssetType;
|
||||
result += String.Format("[Item] Name: {0} Desc: {1} Type: {2}", item.Name, item.Description,
|
||||
assetType);
|
||||
if (assetType == AssetType.LSLBytecode || assetType == AssetType.LSLText)
|
||||
{
|
||||
OnScriptRunningReset.Reset();
|
||||
Client.Inventory.RequestGetScriptRunning(objectID, item.UUID);
|
||||
if (!OnScriptRunningReset.WaitOne(10000, true))
|
||||
{
|
||||
result += " (no script info)";
|
||||
}
|
||||
if (setAny && item.Name.Contains(matching))
|
||||
{
|
||||
if (wasRunning != setTaskTo)
|
||||
{
|
||||
OnScriptRunningReset.Reset();
|
||||
result += " Setting " + setTaskTo + " => ";
|
||||
Client.Inventory.RequestSetScriptRunning(objectID, item.UUID, setTaskTo);
|
||||
if (!OnScriptRunningReset.WaitOne(10000, true))
|
||||
{
|
||||
result += " (was not set)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result += Environment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
Client.Inventory.ScriptRunningReply -= callback;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to download task inventory for " + objectLocalID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class TreeCommand: Command
|
||||
{
|
||||
public TreeCommand(TestClient testClient)
|
||||
{
|
||||
Name = "tree";
|
||||
Description = "Rez a tree.";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
string treeName = args[0].Trim(new char[] { ' ' });
|
||||
Tree tree = (Tree)Enum.Parse(typeof(Tree), treeName);
|
||||
|
||||
Vector3 treePosition = Client.Self.SimPosition;
|
||||
treePosition.Z += 3.0f;
|
||||
|
||||
Client.Objects.AddTree(Client.Network.CurrentSim, new Vector3(0.5f, 0.5f, 0.5f),
|
||||
Quaternion.Identity, treePosition, tree, Client.GroupID, false);
|
||||
|
||||
return "Attempted to rez a " + treeName + " tree";
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "Type !tree for usage";
|
||||
}
|
||||
}
|
||||
|
||||
string usage = "Usage: !tree [";
|
||||
foreach (string value in Enum.GetNames(typeof(Tree)))
|
||||
{
|
||||
usage += value + ",";
|
||||
}
|
||||
usage = usage.TrimEnd(new char[] { ',' });
|
||||
usage += "]";
|
||||
return usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Drawing;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Http;
|
||||
using OpenMetaverse.Imaging;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class UploadImageCommand : Command
|
||||
{
|
||||
AutoResetEvent UploadCompleteEvent = new AutoResetEvent(false);
|
||||
UUID TextureID = UUID.Zero;
|
||||
DateTime start;
|
||||
|
||||
public UploadImageCommand(TestClient testClient)
|
||||
{
|
||||
Name = "uploadimage";
|
||||
Description = "Upload an image to your inventory. Usage: uploadimage [inventoryname] [timeout] [filename]";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
string inventoryName;
|
||||
uint timeout;
|
||||
string fileName;
|
||||
|
||||
if (args.Length != 3)
|
||||
return "Usage: uploadimage [inventoryname] [timeout] [filename]";
|
||||
|
||||
TextureID = UUID.Zero;
|
||||
inventoryName = args[0];
|
||||
fileName = args[2];
|
||||
if (!UInt32.TryParse(args[1], out timeout))
|
||||
return "Usage: uploadimage [inventoryname] [timeout] [filename]";
|
||||
|
||||
Console.WriteLine("Loading image " + fileName);
|
||||
byte[] jpeg2k = LoadImage(fileName);
|
||||
if (jpeg2k == null)
|
||||
return "Failed to compress image to JPEG2000";
|
||||
Console.WriteLine("Finished compressing image to JPEG2000, uploading...");
|
||||
start = DateTime.Now;
|
||||
DoUpload(jpeg2k, inventoryName);
|
||||
|
||||
if (UploadCompleteEvent.WaitOne((int)timeout, false))
|
||||
{
|
||||
return String.Format("Texture upload {0}: {1}", (TextureID != UUID.Zero) ? "succeeded" : "failed",
|
||||
TextureID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Texture upload timed out";
|
||||
}
|
||||
}
|
||||
|
||||
private void DoUpload(byte[] UploadData, string FileName)
|
||||
{
|
||||
if (UploadData != null)
|
||||
{
|
||||
string name = System.IO.Path.GetFileNameWithoutExtension(FileName);
|
||||
|
||||
Client.Inventory.RequestCreateItemFromAsset(UploadData, name, "Uploaded with TestClient",
|
||||
AssetType.Texture, InventoryType.Texture, Client.Inventory.FindFolderForType(AssetType.Texture),
|
||||
delegate(bool success, string status, UUID itemID, UUID assetID)
|
||||
{
|
||||
Console.WriteLine(String.Format(
|
||||
"RequestCreateItemFromAsset() returned: Success={0}, Status={1}, ItemID={2}, AssetID={3}",
|
||||
success, status, itemID, assetID));
|
||||
|
||||
TextureID = assetID;
|
||||
Console.WriteLine(String.Format("Upload took {0}", DateTime.Now.Subtract(start)));
|
||||
UploadCompleteEvent.Set();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] LoadImage(string fileName)
|
||||
{
|
||||
byte[] UploadData;
|
||||
string lowfilename = fileName.ToLower();
|
||||
Bitmap bitmap = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (lowfilename.EndsWith(".jp2") || lowfilename.EndsWith(".j2c"))
|
||||
{
|
||||
Image image;
|
||||
ManagedImage managedImage;
|
||||
|
||||
// Upload JPEG2000 images untouched
|
||||
UploadData = System.IO.File.ReadAllBytes(fileName);
|
||||
|
||||
OpenJPEG.DecodeToImage(UploadData, out managedImage, out image);
|
||||
bitmap = (Bitmap)image;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lowfilename.EndsWith(".tga"))
|
||||
bitmap = LoadTGAClass.LoadTGA(fileName);
|
||||
else
|
||||
bitmap = (Bitmap)System.Drawing.Image.FromFile(fileName);
|
||||
|
||||
int oldwidth = bitmap.Width;
|
||||
int oldheight = bitmap.Height;
|
||||
|
||||
if (!IsPowerOfTwo((uint)oldwidth) || !IsPowerOfTwo((uint)oldheight))
|
||||
{
|
||||
Bitmap resized = new Bitmap(256, 256, bitmap.PixelFormat);
|
||||
Graphics graphics = Graphics.FromImage(resized);
|
||||
|
||||
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
||||
graphics.InterpolationMode =
|
||||
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
||||
graphics.DrawImage(bitmap, 0, 0, 256, 256);
|
||||
|
||||
bitmap.Dispose();
|
||||
bitmap = resized;
|
||||
|
||||
oldwidth = 256;
|
||||
oldheight = 256;
|
||||
}
|
||||
|
||||
// Handle resizing to prevent excessively large images
|
||||
if (oldwidth > 1024 || oldheight > 1024)
|
||||
{
|
||||
int newwidth = (oldwidth > 1024) ? 1024 : oldwidth;
|
||||
int newheight = (oldheight > 1024) ? 1024 : oldheight;
|
||||
|
||||
Bitmap resized = new Bitmap(newwidth, newheight, bitmap.PixelFormat);
|
||||
Graphics graphics = Graphics.FromImage(resized);
|
||||
|
||||
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
||||
graphics.InterpolationMode =
|
||||
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
||||
graphics.DrawImage(bitmap, 0, 0, newwidth, newheight);
|
||||
|
||||
bitmap.Dispose();
|
||||
bitmap = resized;
|
||||
}
|
||||
|
||||
UploadData = OpenJPEG.EncodeFromImage(bitmap, false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString() + " SL Image Upload ");
|
||||
return null;
|
||||
}
|
||||
return UploadData;
|
||||
}
|
||||
|
||||
private static bool IsPowerOfTwo(uint n)
|
||||
{
|
||||
return (n & (n - 1)) == 0 && n != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Example of how to put a new script in your inventory
|
||||
/// </summary>
|
||||
public class UploadScriptCommand : Command
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for TestClient commands
|
||||
/// </summary>
|
||||
/// <param name="testClient"></param>
|
||||
public UploadScriptCommand(TestClient testClient)
|
||||
{
|
||||
Name = "uploadscript";
|
||||
Description = "Upload a local .lsl file file into your inventory.";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default override for TestClient commands
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="fromAgentID"></param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: uploadscript filename.lsl";
|
||||
|
||||
string file = String.Empty;
|
||||
for (int ct = 0; ct < args.Length; ct++)
|
||||
file = String.Format("{0}{1} ", file, args[ct]);
|
||||
file = file.TrimEnd();
|
||||
|
||||
if (!File.Exists(file))
|
||||
return String.Format("Filename '{0}' does not exist", file);
|
||||
|
||||
string ret = String.Format("Filename: {0}", file);
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(file))
|
||||
{
|
||||
string body = reader.ReadToEnd();
|
||||
string desc = String.Format("{0} created by OpenMetaverse TestClient {1}", file, DateTime.Now);
|
||||
// create the asset
|
||||
Client.Inventory.RequestCreateItem(Client.Inventory.FindFolderForType(AssetType.LSLText), file, desc, AssetType.LSLText, UUID.Random(), InventoryType.LSL, PermissionMask.All,
|
||||
delegate(bool success, InventoryItem item)
|
||||
{
|
||||
if (success)
|
||||
// upload the asset
|
||||
Client.Inventory.RequestUpdateScriptAgentInventory(EncodeScript(body), item.UUID, true, new InventoryManager.ScriptUpdatedCallback(delegate(bool uploadSuccess, string uploadStatus, bool compileSuccess, List<string> compileMessages, UUID itemid, UUID assetid)
|
||||
{
|
||||
if (uploadSuccess)
|
||||
ret += String.Format(" Script successfully uploaded, ItemID {0} AssetID {1}", itemid, assetid);
|
||||
if (compileSuccess)
|
||||
ret += " compilation successful";
|
||||
|
||||
}));
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Logger.Log(e.ToString(), Helpers.LogLevel.Error, Client);
|
||||
return String.Format("Error creating script for {0}", ret);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Encodes the script text for uploading
|
||||
/// </summary>
|
||||
/// <param name="body"></param>
|
||||
public static byte[] EncodeScript(string body)
|
||||
{
|
||||
// Assume this is a string, add 1 for the null terminator ?
|
||||
byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(body);
|
||||
byte[] assetData = new byte[stringBytes.Length]; //+ 1];
|
||||
Array.Copy(stringBytes, 0, assetData, 0, stringBytes.Length);
|
||||
return assetData;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ViewNotecardCommand : Command
|
||||
{
|
||||
/// <summary>
|
||||
/// TestClient command to download and display a notecard asset
|
||||
/// </summary>
|
||||
/// <param name="testClient"></param>
|
||||
public ViewNotecardCommand(TestClient testClient)
|
||||
{
|
||||
Name = "viewnote";
|
||||
Description = "Downloads and displays a notecard asset";
|
||||
Category = CommandCategory.Inventory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exectute the command
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="fromAgentID"></param>
|
||||
/// <returns></returns>
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
return "Usage: viewnote [notecard asset uuid]";
|
||||
}
|
||||
UUID note;
|
||||
if (!UUID.TryParse(args[0], out note))
|
||||
{
|
||||
return "First argument expected agent UUID.";
|
||||
}
|
||||
|
||||
System.Threading.AutoResetEvent waitEvent = new System.Threading.AutoResetEvent(false);
|
||||
|
||||
System.Text.StringBuilder result = new System.Text.StringBuilder();
|
||||
|
||||
// verify asset is loaded in store
|
||||
if (Client.Inventory.Store.Contains(note))
|
||||
{
|
||||
// retrieve asset from store
|
||||
InventoryItem ii = (InventoryItem)Client.Inventory.Store[note];
|
||||
|
||||
// make request for asset
|
||||
Client.Assets.RequestInventoryAsset(ii, true,
|
||||
delegate(AssetDownload transfer, Asset asset)
|
||||
{
|
||||
if (transfer.Success)
|
||||
{
|
||||
result.AppendFormat("Raw Notecard Data: " + System.Environment.NewLine + " {0}", Utils.BytesToString(asset.AssetData));
|
||||
waitEvent.Set();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// wait for reply or timeout
|
||||
if (!waitEvent.WaitOne(10000, false))
|
||||
{
|
||||
result.Append("Timeout waiting for notecard to download.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append("Cannot find asset in inventory store, use 'i' to populate store");
|
||||
}
|
||||
|
||||
// return results
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class XferCommand : Command
|
||||
{
|
||||
const int FETCH_ASSET_TIMEOUT = 1000 * 10;
|
||||
|
||||
public XferCommand(TestClient testClient)
|
||||
{
|
||||
Name = "xfer";
|
||||
Description = "Downloads the specified asset using the Xfer system. Usage: xfer [uuid]";
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
UUID assetID;
|
||||
|
||||
if (args.Length != 1 || !UUID.TryParse(args[0], out assetID))
|
||||
return "Usage: xfer [uuid]";
|
||||
|
||||
string filename;
|
||||
byte[] assetData = RequestXfer(assetID, AssetType.Object, out filename);
|
||||
|
||||
if (assetData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(filename, assetData);
|
||||
return "Saved asset " + filename;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return "Failed to save asset " + filename + ": " + ex.Message;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to xfer asset " + assetID;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] RequestXfer(UUID assetID, AssetType type, out string filename)
|
||||
{
|
||||
AutoResetEvent xferEvent = new AutoResetEvent(false);
|
||||
ulong xferID = 0;
|
||||
byte[] data = null;
|
||||
|
||||
EventHandler<XferReceivedEventArgs> xferCallback =
|
||||
delegate(object sender, XferReceivedEventArgs e)
|
||||
{
|
||||
if (e.Xfer.XferID == xferID)
|
||||
{
|
||||
if (e.Xfer.Success)
|
||||
data = e.Xfer.AssetData;
|
||||
xferEvent.Set();
|
||||
}
|
||||
};
|
||||
|
||||
Client.Assets.XferReceived += xferCallback;
|
||||
|
||||
filename = assetID + ".asset";
|
||||
xferID = Client.Assets.RequestAssetXfer(filename, false, true, assetID, type, false);
|
||||
|
||||
xferEvent.WaitOne(FETCH_ASSET_TIMEOUT, false);
|
||||
|
||||
Client.Assets.XferReceived -= xferCallback;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Display a list of all agent locations in a specified region
|
||||
/// </summary>
|
||||
public class AgentLocationsCommand : Command
|
||||
{
|
||||
public AgentLocationsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "agentlocations";
|
||||
Description = "Downloads all of the agent locations in a specified region. Usage: agentlocations [regionhandle]";
|
||||
Category = CommandCategory.Simulator;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
ulong regionHandle;
|
||||
|
||||
if (args.Length == 0)
|
||||
regionHandle = Client.Network.CurrentSim.Handle;
|
||||
else if (!(args.Length == 1 && UInt64.TryParse(args[0], out regionHandle)))
|
||||
return "Usage: agentlocations [regionhandle]";
|
||||
|
||||
List<MapItem> items = Client.Grid.MapItems(regionHandle, GridItemType.AgentLocations,
|
||||
GridLayerType.Objects, 1000 * 20);
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
StringBuilder ret = new StringBuilder();
|
||||
ret.AppendLine("Agent locations:");
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
MapAgentLocation location = (MapAgentLocation)items[i];
|
||||
|
||||
ret.AppendLine(String.Format("{0} avatar(s) at {1},{2}", location.AvatarCount, location.LocalX,
|
||||
location.LocalY));
|
||||
}
|
||||
|
||||
return ret.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to fetch agent locations";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class FindSimCommand : Command
|
||||
{
|
||||
public FindSimCommand(TestClient testClient)
|
||||
{
|
||||
Name = "findsim";
|
||||
Description = "Searches for a simulator and returns information about it. Usage: findsim [Simulator Name]";
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: findsim [Simulator Name]";
|
||||
|
||||
// Build the simulator name from the args list
|
||||
string simName = string.Empty;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
simName += args[i] + " ";
|
||||
simName = simName.TrimEnd().ToLower();
|
||||
|
||||
//if (!GridDataCached[Client])
|
||||
//{
|
||||
// Client.Grid.RequestAllSims(GridManager.MapLayerType.Objects);
|
||||
// System.Threading.Thread.Sleep(5000);
|
||||
// GridDataCached[Client] = true;
|
||||
//}
|
||||
|
||||
GridRegion region;
|
||||
|
||||
if (Client.Grid.GetGridRegion(simName, GridLayerType.Objects, out region))
|
||||
return String.Format("{0}: handle={1} ({2},{3})", region.Name, region.RegionHandle, region.X, region.Y);
|
||||
else
|
||||
return "Lookup of " + simName + " failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GridLayerCommand : Command
|
||||
{
|
||||
public GridLayerCommand(TestClient testClient)
|
||||
{
|
||||
Name = "gridlayer";
|
||||
Description = "Downloads all of the layer chunks for the grid object map";
|
||||
Category = CommandCategory.Simulator;
|
||||
|
||||
testClient.Grid.GridLayer += Grid_GridLayer;
|
||||
}
|
||||
|
||||
void Grid_GridLayer(object sender, GridLayerEventArgs e)
|
||||
{
|
||||
Console.WriteLine(String.Format("Layer({0}) Bottom: {1} Left: {2} Top: {3} Right: {4}",
|
||||
e.Layer.ImageID.ToString(), e.Layer.Bottom, e.Layer.Left, e.Layer.Top, e.Layer.Right));
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Grid.RequestMapLayer(GridLayerType.Objects);
|
||||
|
||||
return "Sent.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GridMapCommand : Command
|
||||
{
|
||||
public GridMapCommand(TestClient testClient)
|
||||
{
|
||||
Name = "gridmap";
|
||||
Description = "Downloads all visible information about the grid map";
|
||||
Category = CommandCategory.Simulator;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
//if (args.Length < 1)
|
||||
// return "";
|
||||
|
||||
Client.Grid.RequestMainlandSims(GridLayerType.Objects);
|
||||
|
||||
return "Sent.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ParcelDetailsCommand : Command
|
||||
{
|
||||
public ParcelDetailsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "parceldetails";
|
||||
Description = "Displays parcel details from the ParcelTracker dictionary. Usage: parceldetails parcelID";
|
||||
Category = CommandCategory.Parcel;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: parceldetails parcelID (use parcelinfo to get ID)";
|
||||
|
||||
int parcelID;
|
||||
Parcel parcel;
|
||||
|
||||
// test argument that is is a valid integer, then verify we have that parcel data stored in the dictionary
|
||||
if (Int32.TryParse(args[0], out parcelID) && Client.Network.CurrentSim.Parcels.TryGetValue(parcelID, out parcel))
|
||||
{
|
||||
// this request will update the parcels dictionary
|
||||
Client.Parcels.RequestParcelProperties(Client.Network.CurrentSim, parcelID, 0);
|
||||
|
||||
// Use reflection to dynamically get the fields from the Parcel struct
|
||||
Type t = parcel.GetType();
|
||||
System.Reflection.FieldInfo[] fields = t.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (System.Reflection.FieldInfo field in fields)
|
||||
{
|
||||
sb.AppendFormat("{0} = {1}" + System.Environment.NewLine, field.Name, field.GetValue(parcel));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Format("Unable to find Parcel {0} in Parcels Dictionary, Did you run parcelinfo to populate the dictionary first?", args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ParcelInfoCommand : Command
|
||||
{
|
||||
private AutoResetEvent ParcelsDownloaded = new AutoResetEvent(false);
|
||||
|
||||
public ParcelInfoCommand(TestClient testClient)
|
||||
{
|
||||
Name = "parcelinfo";
|
||||
Description = "Prints out info about all the parcels in this simulator";
|
||||
Category = CommandCategory.Parcel;
|
||||
|
||||
testClient.Network.Disconnected += Network_OnDisconnected;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
string result;
|
||||
EventHandler<SimParcelsDownloadedEventArgs> del = delegate(object sender, SimParcelsDownloadedEventArgs e)
|
||||
{
|
||||
ParcelsDownloaded.Set();
|
||||
};
|
||||
|
||||
|
||||
ParcelsDownloaded.Reset();
|
||||
Client.Parcels.SimParcelsDownloaded += del;
|
||||
Client.Parcels.RequestAllSimParcels(Client.Network.CurrentSim);
|
||||
|
||||
if (Client.Network.CurrentSim.IsParcelMapFull())
|
||||
ParcelsDownloaded.Set();
|
||||
|
||||
if (ParcelsDownloaded.WaitOne(30000, false) && Client.Network.Connected)
|
||||
{
|
||||
sb.AppendFormat("Downloaded {0} Parcels in {1} " + System.Environment.NewLine,
|
||||
Client.Network.CurrentSim.Parcels.Count, Client.Network.CurrentSim.Name);
|
||||
|
||||
Client.Network.CurrentSim.Parcels.ForEach(delegate(Parcel parcel)
|
||||
{
|
||||
sb.AppendFormat("Parcel[{0}]: Name: \"{1}\", Description: \"{2}\" ACLBlacklist Count: {3}, ACLWhiteList Count: {5} Traffic: {4}" + System.Environment.NewLine,
|
||||
parcel.LocalID, parcel.Name, parcel.Desc, parcel.AccessBlackList.Count, parcel.Dwell, parcel.AccessWhiteList.Count);
|
||||
});
|
||||
|
||||
result = sb.ToString();
|
||||
}
|
||||
else
|
||||
result = "Failed to retrieve information on all the simulator parcels";
|
||||
|
||||
Client.Parcels.SimParcelsDownloaded -= del;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Network_OnDisconnected(object sender, DisconnectedEventArgs e)
|
||||
{
|
||||
ParcelsDownloaded.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ParcelPrimOwnersCommand : Command
|
||||
{
|
||||
public ParcelPrimOwnersCommand(TestClient testClient)
|
||||
{
|
||||
Name = "primowners";
|
||||
Description = "Displays a list of prim owners and prim counts on a parcel. Usage: primowners parcelID";
|
||||
Category = CommandCategory.Parcel;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: primowners parcelID (use parcelinfo to get ID)";
|
||||
|
||||
int parcelID;
|
||||
Parcel parcel;
|
||||
StringBuilder result = new StringBuilder();
|
||||
// test argument that is is a valid integer, then verify we have that parcel data stored in the dictionary
|
||||
if (Int32.TryParse(args[0], out parcelID) && Client.Network.CurrentSim.Parcels.TryGetValue(parcelID, out parcel))
|
||||
{
|
||||
AutoResetEvent wait = new AutoResetEvent(false);
|
||||
|
||||
EventHandler<ParcelObjectOwnersReplyEventArgs> callback = delegate(object sender, ParcelObjectOwnersReplyEventArgs e)
|
||||
{
|
||||
for (int i = 0; i < e.PrimOwners.Count; i++)
|
||||
{
|
||||
result.AppendFormat("Owner: {0} Count: {1}" + System.Environment.NewLine, e.PrimOwners[i].OwnerID, e.PrimOwners[i].Count);
|
||||
wait.Set();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Client.Parcels.ParcelObjectOwnersReply += callback;
|
||||
|
||||
Client.Parcels.RequestObjectOwners(Client.Network.CurrentSim, parcelID);
|
||||
if (!wait.WaitOne(10000, false))
|
||||
{
|
||||
result.AppendLine("Timed out waiting for packet.");
|
||||
}
|
||||
Client.Parcels.ParcelObjectOwnersReply -= callback;
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Format("Unable to find Parcel {0} in Parcels Dictionary, Did you run parcelinfo to populate the dictionary first?", args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ParcelSelectObjectsCommand : Command
|
||||
{
|
||||
public ParcelSelectObjectsCommand(TestClient testClient)
|
||||
{
|
||||
Name = "selectobjects";
|
||||
Description = "Displays a list of prim localIDs on a given parcel with a specific owner. Usage: selectobjects parcelID OwnerUUID";
|
||||
Category = CommandCategory.Parcel;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
return "Usage: selectobjects parcelID OwnerUUID (use parcelinfo to get ID, use parcelprimowners to get ownerUUID)";
|
||||
|
||||
int parcelID;
|
||||
UUID ownerUUID;
|
||||
|
||||
int counter = 0;
|
||||
StringBuilder result = new StringBuilder();
|
||||
// test argument that is is a valid integer, then verify we have that parcel data stored in the dictionary
|
||||
if (Int32.TryParse(args[0], out parcelID)
|
||||
&& UUID.TryParse(args[1], out ownerUUID))
|
||||
{
|
||||
AutoResetEvent wait = new AutoResetEvent(false);
|
||||
EventHandler<ForceSelectObjectsReplyEventArgs> callback = delegate(object sender, ForceSelectObjectsReplyEventArgs e)
|
||||
{
|
||||
|
||||
for (int i = 0; i < e.ObjectIDs.Count; i++)
|
||||
{
|
||||
result.Append(e.ObjectIDs[i].ToString() + " ");
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (e.ObjectIDs.Count < 251)
|
||||
wait.Set();
|
||||
};
|
||||
|
||||
|
||||
Client.Parcels.ForceSelectObjectsReply += callback;
|
||||
Client.Parcels.RequestSelectObjects(parcelID, (ObjectReturnType)16, ownerUUID);
|
||||
|
||||
|
||||
Client.Parcels.RequestObjectOwners(Client.Network.CurrentSim, parcelID);
|
||||
if (!wait.WaitOne(30000, false))
|
||||
{
|
||||
result.AppendLine("Timed out waiting for packet.");
|
||||
}
|
||||
|
||||
Client.Parcels.ForceSelectObjectsReply -= callback;
|
||||
result.AppendLine("Found a total of " + counter + " Objects");
|
||||
return result.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Format("Unable to find Parcel {0} in Parcels Dictionary, Did you run parcelinfo to populate the dictionary first?", args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class WindCommand : Command
|
||||
{
|
||||
public WindCommand(TestClient testClient)
|
||||
{
|
||||
Name = "wind";
|
||||
Description = "Displays current wind data";
|
||||
Category = CommandCategory.Simulator;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// Get the agent's current "patch" position, where each patch of
|
||||
// wind data is a 16x16m square
|
||||
Vector3 agentPos = Client.Self.SimPosition;
|
||||
int xPos = (int)Utils.Clamp(agentPos.X, 0.0f, 255.0f) / 16;
|
||||
int yPos = (int)Utils.Clamp(agentPos.Y, 0.0f, 255.0f) / 16;
|
||||
|
||||
Vector2 windSpeed = Client.Network.CurrentSim.WindSpeeds[yPos * 16 + xPos];
|
||||
|
||||
return "Local wind speed is " + windSpeed.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class BackCommand : Command
|
||||
{
|
||||
public BackCommand(TestClient client)
|
||||
{
|
||||
Name = "back";
|
||||
Description = "Sends the move back command to the server for a single packet or a given number of seconds. Usage: back [seconds]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length > 1)
|
||||
return "Usage: back [seconds]";
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Client.Self.Movement.SendManualUpdate(AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG, Client.Self.Movement.Camera.Position,
|
||||
Client.Self.Movement.Camera.AtAxis, Client.Self.Movement.Camera.LeftAxis, Client.Self.Movement.Camera.UpAxis,
|
||||
Client.Self.Movement.BodyRotation, Client.Self.Movement.HeadRotation, Client.Self.Movement.Camera.Far, AgentFlags.None,
|
||||
AgentState.None, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse the number of seconds
|
||||
int duration;
|
||||
if (!Int32.TryParse(args[0], out duration))
|
||||
return "Usage: back [seconds]";
|
||||
// Convert to milliseconds
|
||||
duration *= 1000;
|
||||
|
||||
int start = Environment.TickCount;
|
||||
|
||||
Client.Self.Movement.AtNeg = true;
|
||||
|
||||
while (Environment.TickCount - start < duration)
|
||||
{
|
||||
// The movement timer will do this automatically, but we do it here as an example
|
||||
// and to make sure updates are being sent out fast enough
|
||||
Client.Self.Movement.SendUpdate(false);
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Client.Self.Movement.AtNeg = false;
|
||||
}
|
||||
|
||||
return "Moved backward";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class CrouchCommand : Command
|
||||
{
|
||||
public CrouchCommand(TestClient testClient)
|
||||
{
|
||||
Name = "crouch";
|
||||
Description = "Starts or stops crouching. Usage: crouch [start/stop]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
bool start = true;
|
||||
|
||||
if (args.Length == 1 && args[0].ToLower() == "stop")
|
||||
start = false;
|
||||
|
||||
if (start)
|
||||
{
|
||||
Client.Self.Crouch(true);
|
||||
return "Started crouching";
|
||||
}
|
||||
else
|
||||
{
|
||||
Client.Self.Crouch(false);
|
||||
return "Stopped crouching";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class FlyCommand : Command
|
||||
{
|
||||
public FlyCommand(TestClient testClient)
|
||||
{
|
||||
Name = "fly";
|
||||
Description = "Starts or stops flying. Usage: fly [start/stop]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
bool start = true;
|
||||
|
||||
if (args.Length == 1 && args[0].ToLower() == "stop")
|
||||
start = false;
|
||||
|
||||
if (start)
|
||||
{
|
||||
Client.Self.Fly(true);
|
||||
return "Started flying";
|
||||
}
|
||||
else
|
||||
{
|
||||
Client.Self.Fly(false);
|
||||
return "Stopped flying";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class FlyToCommand : Command
|
||||
{
|
||||
|
||||
Vector3 myPos = new Vector3();
|
||||
Vector2 myPos0 = new Vector2();
|
||||
Vector3 target = new Vector3();
|
||||
Vector2 target0 = new Vector2();
|
||||
float diff, olddiff, saveolddiff;
|
||||
int startTime = 0;
|
||||
int duration = 10000;
|
||||
bool running = false;
|
||||
|
||||
public FlyToCommand(TestClient Client)
|
||||
{
|
||||
Name = "FlyTo";
|
||||
Description = "Fly the avatar toward the specified position for a maximum of seconds. Usage: FlyTo x y z [seconds]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length > 4 || args.Length < 3)
|
||||
return "Usage: FlyTo x y z [seconds]";
|
||||
|
||||
if (!float.TryParse(args[0], out target.X) ||
|
||||
!float.TryParse(args[1], out target.Y) ||
|
||||
!float.TryParse(args[2], out target.Z))
|
||||
{
|
||||
return "Usage: FlyTo x y z [seconds]";
|
||||
}
|
||||
|
||||
if (running)
|
||||
return "Already in progress, wait for the previous FlyTo to finish";
|
||||
|
||||
running = true;
|
||||
|
||||
// Subscribe to terse update events while this command is running
|
||||
Client.Objects.TerseObjectUpdate += Objects_OnObjectUpdated;
|
||||
|
||||
target0.X = target.X;
|
||||
target0.Y = target.Y;
|
||||
|
||||
if (args.Length == 4 && Int32.TryParse(args[3], out duration))
|
||||
duration *= 1000;
|
||||
|
||||
startTime = Environment.TickCount;
|
||||
Client.Self.Movement.Fly = true;
|
||||
Client.Self.Movement.AtPos = true;
|
||||
Client.Self.Movement.AtNeg = false;
|
||||
ZMovement();
|
||||
Client.Self.Movement.TurnToward(target);
|
||||
|
||||
return string.Format("flying to {0} in {1} seconds", target.ToString(), duration / 1000);
|
||||
}
|
||||
|
||||
private void Objects_OnObjectUpdated(object sender, TerseObjectUpdateEventArgs e)
|
||||
{
|
||||
if (startTime == 0) return;
|
||||
if (e.Update.LocalID == Client.Self.LocalID)
|
||||
{
|
||||
XYMovement();
|
||||
ZMovement();
|
||||
if (Client.Self.Movement.AtPos || Client.Self.Movement.AtNeg)
|
||||
{
|
||||
Client.Self.Movement.TurnToward(target);
|
||||
Debug("Flyxy ");
|
||||
}
|
||||
else if (Client.Self.Movement.UpPos || Client.Self.Movement.UpNeg)
|
||||
{
|
||||
Client.Self.Movement.TurnToward(target);
|
||||
//Client.Self.Movement.SendUpdate(false);
|
||||
Debug("Fly z ");
|
||||
}
|
||||
else if (Vector3.Distance(target, Client.Self.SimPosition) <= 2.0)
|
||||
{
|
||||
EndFlyto();
|
||||
Debug("At Target");
|
||||
}
|
||||
}
|
||||
if (Environment.TickCount - startTime > duration)
|
||||
{
|
||||
EndFlyto();
|
||||
Debug("End Flyto");
|
||||
}
|
||||
}
|
||||
|
||||
private bool XYMovement()
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
myPos = Client.Self.SimPosition;
|
||||
myPos0.X = myPos.X;
|
||||
myPos0.Y = myPos.Y;
|
||||
diff = Vector2.Distance(target0, myPos0);
|
||||
Vector2 vvel = new Vector2(Client.Self.Velocity.X, Client.Self.Velocity.Y);
|
||||
float vel = vvel.Length();
|
||||
if (diff >= 10.0)
|
||||
{
|
||||
Client.Self.Movement.AtPos = true;
|
||||
|
||||
res = true;
|
||||
}
|
||||
else if (diff >= 2 && vel < 5)
|
||||
{
|
||||
Client.Self.Movement.AtPos = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Client.Self.Movement.AtPos = false;
|
||||
Client.Self.Movement.AtNeg = false;
|
||||
}
|
||||
saveolddiff = olddiff;
|
||||
olddiff = diff;
|
||||
return res;
|
||||
}
|
||||
|
||||
private void ZMovement()
|
||||
{
|
||||
Client.Self.Movement.UpPos = false;
|
||||
Client.Self.Movement.UpNeg = false;
|
||||
float diffz = (target.Z - Client.Self.SimPosition.Z);
|
||||
if (diffz >= 20.0)
|
||||
Client.Self.Movement.UpPos = true;
|
||||
else if (diffz <= -20.0)
|
||||
Client.Self.Movement.UpNeg = true;
|
||||
else if (diffz >= +5.0 && Client.Self.Velocity.Z < +4.0)
|
||||
Client.Self.Movement.UpPos = true;
|
||||
else if (diffz <= -5.0 && Client.Self.Velocity.Z > -4.0)
|
||||
Client.Self.Movement.UpNeg = true;
|
||||
else if (diffz >= +2.0 && Client.Self.Velocity.Z < +1.0)
|
||||
Client.Self.Movement.UpPos = true;
|
||||
else if (diffz <= -2.0 && Client.Self.Velocity.Z > -1.0)
|
||||
Client.Self.Movement.UpNeg = true;
|
||||
}
|
||||
|
||||
private void EndFlyto()
|
||||
{
|
||||
// Unsubscribe from terse update events
|
||||
Client.Objects.TerseObjectUpdate -= Objects_OnObjectUpdated;
|
||||
|
||||
startTime = 0;
|
||||
Client.Self.Movement.AtPos = false;
|
||||
Client.Self.Movement.AtNeg = false;
|
||||
Client.Self.Movement.UpPos = false;
|
||||
Client.Self.Movement.UpNeg = false;
|
||||
Client.Self.Movement.SendUpdate(false);
|
||||
|
||||
running = false;
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
private void Debug(string x)
|
||||
{
|
||||
Console.WriteLine(x + " {0,3:##0} {1,3:##0} {2,3:##0} diff {3,5:##0.0} olddiff {4,5:##0.0} At:{5,5} {6,5} Up:{7,5} {8,5} v: {9} w: {10}",
|
||||
myPos.X, myPos.Y, myPos.Z, diff, saveolddiff,
|
||||
Client.Self.Movement.AtPos, Client.Self.Movement.AtNeg, Client.Self.Movement.UpPos, Client.Self.Movement.UpNeg,
|
||||
Client.Self.Velocity.ToString(), Client.Self.AngularVelocity.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class FollowCommand: Command
|
||||
{
|
||||
const float DISTANCE_BUFFER = 3.0f;
|
||||
uint targetLocalID = 0;
|
||||
|
||||
public FollowCommand(TestClient testClient)
|
||||
{
|
||||
Name = "follow";
|
||||
Description = "Follow another avatar. Usage: follow [FirstName LastName]/off.";
|
||||
Category = CommandCategory.Movement;
|
||||
|
||||
testClient.Network.RegisterCallback(PacketType.AlertMessage, AlertMessageHandler);
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// Construct the target name from the passed arguments
|
||||
string target = String.Empty;
|
||||
for (int ct = 0; ct < args.Length; ct++)
|
||||
target = target + args[ct] + " ";
|
||||
target = target.TrimEnd();
|
||||
|
||||
if (target.Length == 0 || target == "off")
|
||||
{
|
||||
Active = false;
|
||||
targetLocalID = 0;
|
||||
Client.Self.AutoPilotCancel();
|
||||
return "Following is off";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Follow(target))
|
||||
return "Following " + target;
|
||||
else
|
||||
return "Unable to follow " + target + ". Client may not be able to see that avatar.";
|
||||
}
|
||||
}
|
||||
|
||||
bool Follow(string name)
|
||||
{
|
||||
lock (Client.Network.Simulators)
|
||||
{
|
||||
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||
{
|
||||
Avatar target = Client.Network.Simulators[i].ObjectsAvatars.Find(
|
||||
delegate(Avatar avatar)
|
||||
{
|
||||
return avatar.Name == name;
|
||||
}
|
||||
);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
targetLocalID = target.LocalID;
|
||||
Active = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Active)
|
||||
{
|
||||
Client.Self.AutoPilotCancel();
|
||||
Active = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Think()
|
||||
{
|
||||
if (Active)
|
||||
{
|
||||
// Find the target position
|
||||
lock (Client.Network.Simulators)
|
||||
{
|
||||
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||
{
|
||||
Avatar targetAv;
|
||||
|
||||
if (Client.Network.Simulators[i].ObjectsAvatars.TryGetValue(targetLocalID, out targetAv))
|
||||
{
|
||||
float distance = 0.0f;
|
||||
|
||||
if (Client.Network.Simulators[i] == Client.Network.CurrentSim)
|
||||
{
|
||||
distance = Vector3.Distance(targetAv.Position, Client.Self.SimPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: Calculate global distances
|
||||
}
|
||||
|
||||
if (distance > DISTANCE_BUFFER)
|
||||
{
|
||||
uint regionX, regionY;
|
||||
Utils.LongToUInts(Client.Network.Simulators[i].Handle, out regionX, out regionY);
|
||||
|
||||
double xTarget = (double)targetAv.Position.X + (double)regionX;
|
||||
double yTarget = (double)targetAv.Position.Y + (double)regionY;
|
||||
double zTarget = targetAv.Position.Z - 2f;
|
||||
|
||||
Logger.DebugLog(String.Format("[Autopilot] {0} meters away from the target, starting autopilot to <{1},{2},{3}>",
|
||||
distance, xTarget, yTarget, zTarget), Client);
|
||||
|
||||
Client.Self.AutoPilot(xTarget, yTarget, zTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are in range of the target and moving, stop moving
|
||||
Client.Self.AutoPilotCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Think();
|
||||
}
|
||||
|
||||
private void AlertMessageHandler(object sender, PacketReceivedEventArgs e)
|
||||
{
|
||||
Packet packet = e.Packet;
|
||||
|
||||
AlertMessagePacket alert = (AlertMessagePacket)packet;
|
||||
string message = Utils.BytesToString(alert.AlertData.Message);
|
||||
|
||||
if (message.Contains("Autopilot cancel"))
|
||||
{
|
||||
Logger.Log("FollowCommand: " + message, Helpers.LogLevel.Info, Client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class ForwardCommand : Command
|
||||
{
|
||||
public ForwardCommand(TestClient client)
|
||||
{
|
||||
Name = "forward";
|
||||
Description = "Sends the move forward command to the server for a single packet or a given number of seconds. Usage: forward [seconds]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length > 1)
|
||||
return "Usage: forward [seconds]";
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Client.Self.Movement.SendManualUpdate(AgentManager.ControlFlags.AGENT_CONTROL_AT_POS, Client.Self.Movement.Camera.Position,
|
||||
Client.Self.Movement.Camera.AtAxis, Client.Self.Movement.Camera.LeftAxis, Client.Self.Movement.Camera.UpAxis,
|
||||
Client.Self.Movement.BodyRotation, Client.Self.Movement.HeadRotation, Client.Self.Movement.Camera.Far, AgentFlags.None,
|
||||
AgentState.None, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse the number of seconds
|
||||
int duration;
|
||||
if (!Int32.TryParse(args[0], out duration))
|
||||
return "Usage: forward [seconds]";
|
||||
// Convert to milliseconds
|
||||
duration *= 1000;
|
||||
|
||||
int start = Environment.TickCount;
|
||||
|
||||
Client.Self.Movement.AtPos = true;
|
||||
|
||||
while (Environment.TickCount - start < duration)
|
||||
{
|
||||
// The movement timer will do this automatically, but we do it here as an example
|
||||
// and to make sure updates are being sent out fast enough
|
||||
Client.Self.Movement.SendUpdate(false);
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Client.Self.Movement.AtPos = false;
|
||||
}
|
||||
|
||||
return "Moved forward";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GoHomeCommand : Command
|
||||
{
|
||||
public GoHomeCommand(TestClient testClient)
|
||||
{
|
||||
Name = "gohome";
|
||||
Description = "Teleports home";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if ( Client.Self.GoHome() ) {
|
||||
return "Teleport Home Succesful";
|
||||
} else {
|
||||
return "Teleport Home Failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GotoCommand: Command
|
||||
{
|
||||
public GotoCommand(TestClient testClient)
|
||||
{
|
||||
Name = "goto";
|
||||
Description = "Teleport to a location (e.g. \"goto Hooper/100/100/30\")";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return "Usage: goto sim/x/y/z";
|
||||
|
||||
string destination = String.Empty;
|
||||
|
||||
// Handle multi-word sim names by combining the arguments
|
||||
foreach (string arg in args)
|
||||
{
|
||||
destination += arg + " ";
|
||||
}
|
||||
destination = destination.Trim();
|
||||
|
||||
string[] tokens = destination.Split(new char[] { '/' });
|
||||
if (tokens.Length != 4)
|
||||
return "Usage: goto sim/x/y/z";
|
||||
|
||||
string sim = tokens[0];
|
||||
float x, y, z;
|
||||
if (!float.TryParse(tokens[1], out x) ||
|
||||
!float.TryParse(tokens[2], out y) ||
|
||||
!float.TryParse(tokens[3], out z))
|
||||
{
|
||||
return "Usage: goto sim/x/y/z";
|
||||
}
|
||||
|
||||
if (Client.Self.Teleport(sim, new Vector3(x, y, z)))
|
||||
return "Teleported to " + Client.Network.CurrentSim;
|
||||
else
|
||||
return "Teleport failed: " + Client.Self.TeleportMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class GotoLandmarkCommand : Command
|
||||
{
|
||||
public GotoLandmarkCommand(TestClient testClient)
|
||||
{
|
||||
Name = "goto_landmark";
|
||||
Description = "Teleports to a Landmark. Usage: goto_landmark [UUID]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
return "Usage: goto_landmark [UUID]";
|
||||
}
|
||||
|
||||
UUID landmark = new UUID();
|
||||
if (!UUID.TryParse(args[0], out landmark))
|
||||
{
|
||||
return "Invalid LLUID";
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Teleporting to " + landmark.ToString());
|
||||
}
|
||||
if (Client.Self.Teleport(landmark))
|
||||
{
|
||||
return "Teleport Succesful";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Teleport Failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class JumpCommand: Command
|
||||
{
|
||||
public JumpCommand(TestClient testClient)
|
||||
{
|
||||
Name = "jump";
|
||||
Description = "Jumps or flies up";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Self.Jump(true);
|
||||
return "Jumped";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class LeftCommand : Command
|
||||
{
|
||||
public LeftCommand(TestClient client)
|
||||
{
|
||||
Name = "left";
|
||||
Description = "Sends the move left command to the server for a single packet or a given number of seconds. Usage: left [seconds]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length > 1)
|
||||
return "Usage: left [seconds]";
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Client.Self.Movement.SendManualUpdate(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS, Client.Self.Movement.Camera.Position,
|
||||
Client.Self.Movement.Camera.AtAxis, Client.Self.Movement.Camera.LeftAxis, Client.Self.Movement.Camera.UpAxis,
|
||||
Client.Self.Movement.BodyRotation, Client.Self.Movement.HeadRotation, Client.Self.Movement.Camera.Far, AgentFlags.None,
|
||||
AgentState.None, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse the number of seconds
|
||||
int duration;
|
||||
if (!Int32.TryParse(args[0], out duration))
|
||||
return "Usage: left [seconds]";
|
||||
// Convert to milliseconds
|
||||
duration *= 1000;
|
||||
|
||||
int start = Environment.TickCount;
|
||||
|
||||
Client.Self.Movement.LeftPos = true;
|
||||
|
||||
while (Environment.TickCount - start < duration)
|
||||
{
|
||||
// The movement timer will do this automatically, but we do it here as an example
|
||||
// and to make sure updates are being sent out fast enough
|
||||
Client.Self.Movement.SendUpdate(false);
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Client.Self.Movement.LeftPos = false;
|
||||
}
|
||||
|
||||
return "Moved left";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class LocationCommand: Command
|
||||
{
|
||||
public LocationCommand(TestClient testClient)
|
||||
{
|
||||
Name = "location";
|
||||
Description = "Show current location of avatar.";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
return "CurrentSim: '" + Client.Network.CurrentSim.ToString() + "' Position: " +
|
||||
Client.Self.SimPosition.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class MovetoCommand : Command
|
||||
{
|
||||
public MovetoCommand(TestClient client)
|
||||
{
|
||||
Name = "moveto";
|
||||
Description = "Moves the avatar to the specified global position using simulator autopilot. Usage: moveto x y z";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 3)
|
||||
return "Usage: moveto x y z";
|
||||
|
||||
uint regionX, regionY;
|
||||
Utils.LongToUInts(Client.Network.CurrentSim.Handle, out regionX, out regionY);
|
||||
|
||||
double x, y, z;
|
||||
if (!Double.TryParse(args[0], out x) ||
|
||||
!Double.TryParse(args[1], out y) ||
|
||||
!Double.TryParse(args[2], out z))
|
||||
{
|
||||
return "Usage: moveto x y z";
|
||||
}
|
||||
|
||||
// Convert the local coordinates to global ones by adding the region handle parts to x and y
|
||||
x += (double)regionX;
|
||||
y += (double)regionY;
|
||||
|
||||
Client.Self.AutoPilot(x, y, z);
|
||||
|
||||
return String.Format("Attempting to move to <{0},{1},{2}>", x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class RightCommand : Command
|
||||
{
|
||||
public RightCommand(TestClient client)
|
||||
{
|
||||
Name = "right";
|
||||
Description = "Sends the move right command to the server for a single packet or a given number of seconds. Usage: right [seconds]";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length > 1)
|
||||
return "Usage: right [seconds]";
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Client.Self.Movement.SendManualUpdate(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, Client.Self.Movement.Camera.Position,
|
||||
Client.Self.Movement.Camera.AtAxis, Client.Self.Movement.Camera.LeftAxis, Client.Self.Movement.Camera.UpAxis,
|
||||
Client.Self.Movement.BodyRotation, Client.Self.Movement.HeadRotation, Client.Self.Movement.Camera.Far, AgentFlags.None,
|
||||
AgentState.None, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse the number of seconds
|
||||
int duration;
|
||||
if (!Int32.TryParse(args[0], out duration))
|
||||
return "Usage: right [seconds]";
|
||||
// Convert to milliseconds
|
||||
duration *= 1000;
|
||||
|
||||
int start = Environment.TickCount;
|
||||
|
||||
Client.Self.Movement.LeftNeg = true;
|
||||
|
||||
while (Environment.TickCount - start < duration)
|
||||
{
|
||||
// The movement timer will do this automatically, but we do it here as an example
|
||||
// and to make sure updates are being sent out fast enough
|
||||
Client.Self.Movement.SendUpdate(false);
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Client.Self.Movement.LeftNeg = false;
|
||||
}
|
||||
|
||||
return "Moved right";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class SetHomeCommand : Command
|
||||
{
|
||||
public SetHomeCommand(TestClient testClient)
|
||||
{
|
||||
Name = "sethome";
|
||||
Description = "Sets home to the current location.";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Self.SetHome();
|
||||
return "Home Set";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class SitCommand: Command
|
||||
{
|
||||
public SitCommand(TestClient testClient)
|
||||
{
|
||||
Name = "sit";
|
||||
Description = "Attempt to sit on the closest prim";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Primitive closest = null;
|
||||
double closestDistance = Double.MaxValue;
|
||||
|
||||
Client.Network.CurrentSim.ObjectsPrimitives.ForEach(
|
||||
delegate(Primitive prim)
|
||||
{
|
||||
float distance = Vector3.Distance(Client.Self.SimPosition, prim.Position);
|
||||
|
||||
if (closest == null || distance < closestDistance)
|
||||
{
|
||||
closest = prim;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (closest != null)
|
||||
{
|
||||
Client.Self.RequestSit(closest.ID, Vector3.Zero);
|
||||
Client.Self.Sit();
|
||||
|
||||
return "Sat on " + closest.ID + " (" + closest.LocalID + "). Distance: " + closestDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Couldn't find a nearby prim to sit on";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class SitOnCommand : Command
|
||||
{
|
||||
public SitOnCommand(TestClient testClient)
|
||||
{
|
||||
Name = "siton";
|
||||
Description = "Attempt to sit on a particular prim, with specified UUID";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return "Usage: siton UUID";
|
||||
|
||||
UUID target;
|
||||
|
||||
if (UUID.TryParse(args[0], out target))
|
||||
{
|
||||
Primitive targetPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find(
|
||||
delegate(Primitive prim)
|
||||
{
|
||||
return prim.ID == target;
|
||||
}
|
||||
);
|
||||
|
||||
if (targetPrim != null)
|
||||
{
|
||||
Client.Self.RequestSit(targetPrim.ID, Vector3.Zero);
|
||||
Client.Self.Sit();
|
||||
return "Requested to sit on prim " + targetPrim.ID.ToString() +
|
||||
" (" + targetPrim.LocalID + ")";
|
||||
}
|
||||
}
|
||||
|
||||
return "Couldn't find a prim to sit on with UUID " + args[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class StandCommand: Command
|
||||
{
|
||||
public StandCommand(TestClient testClient)
|
||||
{
|
||||
Name = "stand";
|
||||
Description = "Stand";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
Client.Self.Stand();
|
||||
return "Standing up.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
namespace OpenMetaverse.TestClient.Commands.Movement
|
||||
{
|
||||
class TurnToCommand : Command
|
||||
{
|
||||
public TurnToCommand(TestClient client)
|
||||
{
|
||||
Name = "turnto";
|
||||
Description = "Turns the avatar looking to a specified point. Usage: turnto x y z";
|
||||
Category = CommandCategory.Movement;
|
||||
}
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 3)
|
||||
return "Usage: turnto x y z";
|
||||
double x, y, z;
|
||||
if (!Double.TryParse(args[0], out x) ||
|
||||
!Double.TryParse(args[1], out y) ||
|
||||
!Double.TryParse(args[2], out z))
|
||||
{
|
||||
return "Usage: turnto x y z";
|
||||
}
|
||||
|
||||
Vector3 newDirection;
|
||||
newDirection.X = (float)x;
|
||||
newDirection.Y = (float)y;
|
||||
newDirection.Z = (float)z;
|
||||
Client.Self.Movement.TurnToward(newDirection);
|
||||
Client.Self.Movement.SendUpdate(false);
|
||||
return "Turned to ";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ChangePermsCommand : Command
|
||||
{
|
||||
AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false);
|
||||
Dictionary<UUID, Primitive> Objects = new Dictionary<UUID, Primitive>();
|
||||
PermissionMask Perms = PermissionMask.None;
|
||||
private bool PermsSent;
|
||||
private int PermCount;
|
||||
|
||||
public ChangePermsCommand(TestClient testClient)
|
||||
{
|
||||
testClient.Objects.ObjectProperties += new EventHandler<ObjectPropertiesEventArgs>(Objects_OnObjectProperties);
|
||||
|
||||
Name = "changeperms";
|
||||
Description = "Recursively changes all of the permissions for child and task inventory objects. Usage prim-uuid [copy] [mod] [xfer]";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
UUID rootID;
|
||||
Primitive rootPrim;
|
||||
List<Primitive> childPrims;
|
||||
List<uint> localIDs = new List<uint>();
|
||||
|
||||
// Reset class-wide variables
|
||||
PermsSent = false;
|
||||
Objects.Clear();
|
||||
Perms = PermissionMask.None;
|
||||
PermCount = 0;
|
||||
|
||||
if (args.Length < 1 || args.Length > 4)
|
||||
return "Usage prim-uuid [copy] [mod] [xfer]";
|
||||
|
||||
if (!UUID.TryParse(args[0], out rootID))
|
||||
return "Usage prim-uuid [copy] [mod] [xfer]";
|
||||
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i].ToLower())
|
||||
{
|
||||
case "copy":
|
||||
Perms |= PermissionMask.Copy;
|
||||
break;
|
||||
case "mod":
|
||||
Perms |= PermissionMask.Modify;
|
||||
break;
|
||||
case "xfer":
|
||||
Perms |= PermissionMask.Transfer;
|
||||
break;
|
||||
default:
|
||||
return "Usage prim-uuid [copy] [mod] [xfer]";
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Using PermissionMask: " + Perms.ToString(), Client);
|
||||
|
||||
// Find the requested prim
|
||||
rootPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find(delegate(Primitive prim) { return prim.ID == rootID; });
|
||||
if (rootPrim == null)
|
||||
return "Cannot find requested prim " + rootID.ToString();
|
||||
else
|
||||
Logger.DebugLog("Found requested prim " + rootPrim.ID.ToString(), Client);
|
||||
|
||||
if (rootPrim.ParentID != 0)
|
||||
{
|
||||
// This is not actually a root prim, find the root
|
||||
if (!Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(rootPrim.ParentID, out rootPrim))
|
||||
return "Cannot find root prim for requested object";
|
||||
else
|
||||
Logger.DebugLog("Set root prim to " + rootPrim.ID.ToString(), Client);
|
||||
}
|
||||
|
||||
// Find all of the child objects linked to this root
|
||||
childPrims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(delegate(Primitive prim) { return prim.ParentID == rootPrim.LocalID; });
|
||||
|
||||
// Build a dictionary of primitives for referencing later
|
||||
Objects[rootPrim.ID] = rootPrim;
|
||||
for (int i = 0; i < childPrims.Count; i++)
|
||||
Objects[childPrims[i].ID] = childPrims[i];
|
||||
|
||||
// Build a list of all the localIDs to set permissions for
|
||||
localIDs.Add(rootPrim.LocalID);
|
||||
for (int i = 0; i < childPrims.Count; i++)
|
||||
localIDs.Add(childPrims[i].LocalID);
|
||||
|
||||
// Go through each of the three main permissions and enable or disable them
|
||||
#region Set Linkset Permissions
|
||||
|
||||
PermCount = 0;
|
||||
if ((Perms & PermissionMask.Modify) == PermissionMask.Modify)
|
||||
Client.Objects.SetPermissions(Client.Network.CurrentSim, localIDs, PermissionWho.NextOwner, PermissionMask.Modify, true);
|
||||
else
|
||||
Client.Objects.SetPermissions(Client.Network.CurrentSim, localIDs, PermissionWho.NextOwner, PermissionMask.Modify, false);
|
||||
PermsSent = true;
|
||||
|
||||
if (!GotPermissionsEvent.WaitOne(1000 * 30, false))
|
||||
return "Failed to set the modify bit, permissions in an unknown state";
|
||||
|
||||
PermCount = 0;
|
||||
if ((Perms & PermissionMask.Copy) == PermissionMask.Copy)
|
||||
Client.Objects.SetPermissions(Client.Network.CurrentSim, localIDs, PermissionWho.NextOwner, PermissionMask.Copy, true);
|
||||
else
|
||||
Client.Objects.SetPermissions(Client.Network.CurrentSim, localIDs, PermissionWho.NextOwner, PermissionMask.Copy, false);
|
||||
PermsSent = true;
|
||||
|
||||
if (!GotPermissionsEvent.WaitOne(1000 * 30, false))
|
||||
return "Failed to set the copy bit, permissions in an unknown state";
|
||||
|
||||
PermCount = 0;
|
||||
if ((Perms & PermissionMask.Transfer) == PermissionMask.Transfer)
|
||||
Client.Objects.SetPermissions(Client.Network.CurrentSim, localIDs, PermissionWho.NextOwner, PermissionMask.Transfer, true);
|
||||
else
|
||||
Client.Objects.SetPermissions(Client.Network.CurrentSim, localIDs, PermissionWho.NextOwner, PermissionMask.Transfer, false);
|
||||
PermsSent = true;
|
||||
|
||||
if (!GotPermissionsEvent.WaitOne(1000 * 30, false))
|
||||
return "Failed to set the transfer bit, permissions in an unknown state";
|
||||
|
||||
#endregion Set Linkset Permissions
|
||||
|
||||
// Check each prim for task inventory and set permissions on the task inventory
|
||||
int taskItems = 0;
|
||||
foreach (Primitive prim in Objects.Values)
|
||||
{
|
||||
if ((prim.Flags & PrimFlags.InventoryEmpty) == 0)
|
||||
{
|
||||
List<InventoryBase> items = Client.Inventory.GetTaskInventory(prim.ID, prim.LocalID, 1000 * 30);
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (!(items[i] is InventoryFolder))
|
||||
{
|
||||
InventoryItem item = (InventoryItem)items[i];
|
||||
item.Permissions.NextOwnerMask = Perms;
|
||||
|
||||
Client.Inventory.UpdateTaskInventory(prim.LocalID, item);
|
||||
++taskItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "Set permissions to " + Perms.ToString() + " on " + localIDs.Count + " objects and " + taskItems + " inventory items";
|
||||
}
|
||||
|
||||
void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e)
|
||||
{
|
||||
if (PermsSent)
|
||||
{
|
||||
if (Objects.ContainsKey(e.Properties.ObjectID))
|
||||
{
|
||||
// FIXME: Confirm the current operation against properties.Permissions.NextOwnerMask
|
||||
|
||||
++PermCount;
|
||||
if (PermCount >= Objects.Count)
|
||||
GotPermissionsEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class DeRezCommand : Command
|
||||
{
|
||||
public DeRezCommand(TestClient testClient)
|
||||
{
|
||||
Name = "derez";
|
||||
Description = "De-Rezes a specified prim. " + "Usage: derez [prim-uuid]";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
UUID primID;
|
||||
|
||||
if (args.Length != 1)
|
||||
return "Usage: derez [prim-uuid]";
|
||||
|
||||
if (UUID.TryParse(args[0], out primID))
|
||||
{
|
||||
Primitive target = Client.Network.CurrentSim.ObjectsPrimitives.Find(
|
||||
delegate(Primitive prim) { return prim.ID == primID; }
|
||||
);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
uint objectLocalID = target.LocalID;
|
||||
Client.Inventory.RequestDeRezToInventory(objectLocalID, DeRezDestination.AgentInventoryTake,
|
||||
Client.Inventory.FindFolderForType(AssetType.TrashFolder),
|
||||
UUID.Random());
|
||||
return "removing " + target;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Could not find prim " + primID.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Usage: derez [prim-uuid]";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class DownloadTextureCommand : Command
|
||||
{
|
||||
UUID TextureID;
|
||||
AutoResetEvent DownloadHandle = new AutoResetEvent(false);
|
||||
AssetTexture Asset;
|
||||
TextureRequestState resultState;
|
||||
|
||||
public DownloadTextureCommand(TestClient testClient)
|
||||
{
|
||||
Name = "downloadtexture";
|
||||
Description = "Downloads the specified texture. " +
|
||||
"Usage: downloadtexture [texture-uuid] [discardlevel]";
|
||||
Category = CommandCategory.Inventory;
|
||||
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1 && args.Length != 2)
|
||||
return "Usage: downloadtexture [texture-uuid] [discardlevel]";
|
||||
|
||||
TextureID = UUID.Zero;
|
||||
DownloadHandle.Reset();
|
||||
Asset = null;
|
||||
|
||||
if (UUID.TryParse(args[0], out TextureID))
|
||||
{
|
||||
int discardLevel = 0;
|
||||
|
||||
if (args.Length > 1)
|
||||
{
|
||||
if (!Int32.TryParse(args[1], out discardLevel))
|
||||
return "Usage: downloadtexture [texture-uuid] [discardlevel]";
|
||||
}
|
||||
|
||||
Client.Assets.RequestImage(TextureID, ImageType.Normal, Assets_OnImageReceived);
|
||||
|
||||
if (DownloadHandle.WaitOne(120 * 1000, false))
|
||||
{
|
||||
if (resultState == TextureRequestState.Finished)
|
||||
{
|
||||
if (Asset != null && Asset.Decode())
|
||||
{
|
||||
try { File.WriteAllBytes(Asset.AssetID + ".jp2", Asset.AssetData); }
|
||||
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
|
||||
|
||||
return String.Format("Saved {0}.jp2 ({1}x{2})", Asset.AssetID, Asset.Image.Width, Asset.Image.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to decode texture " + TextureID.ToString();
|
||||
}
|
||||
}
|
||||
else if (resultState == TextureRequestState.NotFound)
|
||||
{
|
||||
return "Simulator reported texture not found: " + TextureID.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Download failed for texture " + TextureID + " " + resultState;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Timed out waiting for texture download";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Usage: downloadtexture [texture-uuid]";
|
||||
}
|
||||
}
|
||||
|
||||
private void Assets_OnImageReceived(TextureRequestState state, AssetTexture asset)
|
||||
{
|
||||
resultState = state;
|
||||
Asset = asset;
|
||||
|
||||
DownloadHandle.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Assets;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ExportCommand : Command
|
||||
{
|
||||
List<UUID> Textures = new List<UUID>();
|
||||
AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false);
|
||||
Primitive.ObjectProperties Properties;
|
||||
bool GotPermissions = false;
|
||||
UUID SelectedObject = UUID.Zero;
|
||||
|
||||
Dictionary<UUID, Primitive> PrimsWaiting = new Dictionary<UUID, Primitive>();
|
||||
AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false);
|
||||
|
||||
public ExportCommand(TestClient testClient)
|
||||
{
|
||||
testClient.Objects.ObjectPropertiesFamily += new EventHandler<ObjectPropertiesFamilyEventArgs>(Objects_OnObjectPropertiesFamily);
|
||||
|
||||
testClient.Objects.ObjectProperties += new EventHandler<ObjectPropertiesEventArgs>(Objects_OnObjectProperties);
|
||||
testClient.Avatars.ViewerEffectPointAt += new EventHandler<ViewerEffectPointAtEventArgs>(Avatars_ViewerEffectPointAt);
|
||||
|
||||
Name = "export";
|
||||
Description = "Exports an object to an xml file. Usage: export uuid outputfile.xml";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
void Avatars_ViewerEffectPointAt(object sender, ViewerEffectPointAtEventArgs e)
|
||||
{
|
||||
if (e.SourceID == Client.MasterKey)
|
||||
{
|
||||
//Client.DebugLog("Master is now selecting " + targetID.ToString());
|
||||
SelectedObject = e.TargetID;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 2 && !(args.Length == 1 && SelectedObject != UUID.Zero))
|
||||
return "Usage: export uuid outputfile.xml";
|
||||
|
||||
UUID id;
|
||||
uint localid;
|
||||
string file;
|
||||
|
||||
if (args.Length == 2)
|
||||
{
|
||||
file = args[1];
|
||||
if (!UUID.TryParse(args[0], out id))
|
||||
return "Usage: export uuid outputfile.xml";
|
||||
}
|
||||
else
|
||||
{
|
||||
file = args[0];
|
||||
id = SelectedObject;
|
||||
}
|
||||
|
||||
Primitive exportPrim;
|
||||
|
||||
exportPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find(
|
||||
delegate(Primitive prim) { return prim.ID == id; }
|
||||
);
|
||||
|
||||
if (exportPrim != null)
|
||||
{
|
||||
if (exportPrim.ParentID != 0)
|
||||
localid = exportPrim.ParentID;
|
||||
else
|
||||
localid = exportPrim.LocalID;
|
||||
|
||||
// Check for export permission first
|
||||
Client.Objects.RequestObjectPropertiesFamily(Client.Network.CurrentSim, id);
|
||||
GotPermissionsEvent.WaitOne(1000 * 10, false);
|
||||
|
||||
if (!GotPermissions)
|
||||
{
|
||||
return "Couldn't fetch permissions for the requested object, try again";
|
||||
}
|
||||
else
|
||||
{
|
||||
GotPermissions = false;
|
||||
if (Properties.OwnerID != Client.Self.AgentID &&
|
||||
Properties.OwnerID != Client.MasterKey &&
|
||||
Client.Self.AgentID != Client.Self.AgentID)
|
||||
{
|
||||
return "That object is owned by " + Properties.OwnerID + ", we don't have permission " +
|
||||
"to export it";
|
||||
}
|
||||
}
|
||||
|
||||
List<Primitive> prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(
|
||||
delegate(Primitive prim)
|
||||
{
|
||||
return (prim.LocalID == localid || prim.ParentID == localid);
|
||||
}
|
||||
);
|
||||
|
||||
bool complete = RequestObjectProperties(prims, 250);
|
||||
|
||||
if (!complete)
|
||||
{
|
||||
Logger.Log("Warning: Unable to retrieve full properties for:", Helpers.LogLevel.Warning, Client);
|
||||
foreach (UUID uuid in PrimsWaiting.Keys)
|
||||
Logger.Log(uuid.ToString(), Helpers.LogLevel.Warning, Client);
|
||||
}
|
||||
|
||||
string output = OSDParser.SerializeLLSDXmlString(Helpers.PrimListToOSD(prims));
|
||||
try { File.WriteAllText(file, output); }
|
||||
catch (Exception e) { return e.Message; }
|
||||
|
||||
Logger.Log("Exported " + prims.Count + " prims to " + file, Helpers.LogLevel.Info, Client);
|
||||
|
||||
// Create a list of all of the textures to download
|
||||
List<ImageRequest> textureRequests = new List<ImageRequest>();
|
||||
|
||||
lock (Textures)
|
||||
{
|
||||
for (int i = 0; i < prims.Count; i++)
|
||||
{
|
||||
Primitive prim = prims[i];
|
||||
|
||||
if (prim.Textures.DefaultTexture.TextureID != Primitive.TextureEntry.WHITE_TEXTURE &&
|
||||
!Textures.Contains(prim.Textures.DefaultTexture.TextureID))
|
||||
{
|
||||
Textures.Add(prim.Textures.DefaultTexture.TextureID);
|
||||
}
|
||||
|
||||
for (int j = 0; j < prim.Textures.FaceTextures.Length; j++)
|
||||
{
|
||||
if (prim.Textures.FaceTextures[j] != null &&
|
||||
prim.Textures.FaceTextures[j].TextureID != Primitive.TextureEntry.WHITE_TEXTURE &&
|
||||
!Textures.Contains(prim.Textures.FaceTextures[j].TextureID))
|
||||
{
|
||||
Textures.Add(prim.Textures.FaceTextures[j].TextureID);
|
||||
}
|
||||
}
|
||||
|
||||
if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero && !Textures.Contains(prim.Sculpt.SculptTexture))
|
||||
{
|
||||
Textures.Add(prim.Sculpt.SculptTexture);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a request list from all of the images
|
||||
for (int i = 0; i < Textures.Count; i++)
|
||||
textureRequests.Add(new ImageRequest(Textures[i], ImageType.Normal, 1013000.0f, 0));
|
||||
}
|
||||
|
||||
// Download all of the textures in the export list
|
||||
foreach (ImageRequest request in textureRequests)
|
||||
{
|
||||
Client.Assets.RequestImage(request.ImageID, request.Type, Assets_OnImageReceived);
|
||||
}
|
||||
|
||||
return "XML exported, began downloading " + Textures.Count + " textures";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Couldn't find UUID " + id.ToString() + " in the " +
|
||||
Client.Network.CurrentSim.ObjectsPrimitives.Count +
|
||||
"objects currently indexed in the current simulator";
|
||||
}
|
||||
}
|
||||
|
||||
private bool RequestObjectProperties(List<Primitive> objects, int msPerRequest)
|
||||
{
|
||||
// Create an array of the local IDs of all the prims we are requesting properties for
|
||||
uint[] localids = new uint[objects.Count];
|
||||
|
||||
lock (PrimsWaiting)
|
||||
{
|
||||
PrimsWaiting.Clear();
|
||||
|
||||
for (int i = 0; i < objects.Count; ++i)
|
||||
{
|
||||
localids[i] = objects[i].LocalID;
|
||||
PrimsWaiting.Add(objects[i].ID, objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Client.Objects.SelectObjects(Client.Network.CurrentSim, localids);
|
||||
|
||||
return AllPropertiesReceived.WaitOne(2000 + msPerRequest * objects.Count, false);
|
||||
}
|
||||
|
||||
private void Assets_OnImageReceived(TextureRequestState state, AssetTexture asset)
|
||||
{
|
||||
|
||||
if (state == TextureRequestState.Finished && Textures.Contains(asset.AssetID))
|
||||
{
|
||||
lock (Textures)
|
||||
Textures.Remove(asset.AssetID);
|
||||
|
||||
if (state == TextureRequestState.Finished)
|
||||
{
|
||||
try { File.WriteAllBytes(asset.AssetID + ".jp2", asset.AssetData); }
|
||||
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client); }
|
||||
|
||||
if (asset.Decode())
|
||||
{
|
||||
try { File.WriteAllBytes(asset.AssetID + ".tga", asset.Image.ExportTGA()); }
|
||||
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client); }
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Failed to decode image " + asset.AssetID, Helpers.LogLevel.Error, Client);
|
||||
}
|
||||
|
||||
Logger.Log("Finished downloading image " + asset.AssetID, Helpers.LogLevel.Info, Client);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Failed to download image " + asset.AssetID + ":" + state, Helpers.LogLevel.Warning, Client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Objects_OnObjectPropertiesFamily(object sender, ObjectPropertiesFamilyEventArgs e)
|
||||
{
|
||||
Properties = new Primitive.ObjectProperties();
|
||||
Properties.SetFamilyProperties(e.Properties);
|
||||
GotPermissions = true;
|
||||
GotPermissionsEvent.Set();
|
||||
}
|
||||
|
||||
void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e)
|
||||
{
|
||||
lock (PrimsWaiting)
|
||||
{
|
||||
PrimsWaiting.Remove(e.Properties.ObjectID);
|
||||
|
||||
if (PrimsWaiting.Count == 0)
|
||||
AllPropertiesReceived.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class ExportParticlesCommand : Command
|
||||
{
|
||||
public ExportParticlesCommand(TestClient testClient)
|
||||
{
|
||||
Name = "exportparticles";
|
||||
Description = "Reverse engineers a prim with a particle system to an LSL script. Usage: exportscript [prim-uuid]";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return "Usage: exportparticles [prim-uuid]";
|
||||
|
||||
UUID id;
|
||||
if (!UUID.TryParse(args[0], out id))
|
||||
return "Usage: exportparticles [prim-uuid]";
|
||||
|
||||
lock (Client.Network.Simulators)
|
||||
{
|
||||
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||
{
|
||||
Primitive exportPrim = Client.Network.Simulators[i].ObjectsPrimitives.Find(
|
||||
delegate(Primitive prim)
|
||||
{
|
||||
return prim.ID == id;
|
||||
}
|
||||
);
|
||||
|
||||
if (exportPrim != null)
|
||||
{
|
||||
if (exportPrim.ParticleSys.CRC != 0)
|
||||
{
|
||||
StringBuilder lsl = new StringBuilder();
|
||||
|
||||
#region Particle System to LSL
|
||||
|
||||
lsl.Append("default" + Environment.NewLine);
|
||||
lsl.Append("{" + Environment.NewLine);
|
||||
lsl.Append(" state_entry()" + Environment.NewLine);
|
||||
lsl.Append(" {" + Environment.NewLine);
|
||||
lsl.Append(" llParticleSystem([" + Environment.NewLine);
|
||||
|
||||
lsl.Append(" PSYS_PART_FLAGS, 0");
|
||||
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpColor) != 0)
|
||||
lsl.Append(" | PSYS_PART_INTERP_COLOR_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpScale) != 0)
|
||||
lsl.Append(" | PSYS_PART_INTERP_SCALE_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Bounce) != 0)
|
||||
lsl.Append(" | PSYS_PART_BOUNCE_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Wind) != 0)
|
||||
lsl.Append(" | PSYS_PART_WIND_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowSrc) != 0)
|
||||
lsl.Append(" | PSYS_PART_FOLLOW_SRC_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowVelocity) != 0)
|
||||
lsl.Append(" | PSYS_PART_FOLLOW_VELOCITY_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetPos) != 0)
|
||||
lsl.Append(" | PSYS_PART_TARGET_POS_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetLinear) != 0)
|
||||
lsl.Append(" | PSYS_PART_TARGET_LINEAR_MASK");
|
||||
if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Emissive) != 0)
|
||||
lsl.Append(" | PSYS_PART_EMISSIVE_MASK");
|
||||
|
||||
lsl.Append(","); lsl.Append(Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_PATTERN, 0");
|
||||
|
||||
if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Drop) != 0)
|
||||
lsl.Append(" | PSYS_SRC_PATTERN_DROP");
|
||||
if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Explode) != 0)
|
||||
lsl.Append(" | PSYS_SRC_PATTERN_EXPLODE");
|
||||
if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Angle) != 0)
|
||||
lsl.Append(" | PSYS_SRC_PATTERN_ANGLE");
|
||||
if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleCone) != 0)
|
||||
lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE");
|
||||
if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleConeEmpty) != 0)
|
||||
lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY");
|
||||
|
||||
lsl.Append("," + Environment.NewLine);
|
||||
|
||||
lsl.Append(" PSYS_PART_START_ALPHA, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartStartColor.A) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_PART_END_ALPHA, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartEndColor.A) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_PART_START_COLOR, " + exportPrim.ParticleSys.PartStartColor.ToRGBString() + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_PART_END_COLOR, " + exportPrim.ParticleSys.PartEndColor.ToRGBString() + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_PART_START_SCALE, <" + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartStartScaleX) + ", " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartStartScaleY) + ", 0>, " + Environment.NewLine);
|
||||
lsl.Append(" PSYS_PART_END_SCALE, <" + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartEndScaleX) + ", " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartEndScaleY) + ", 0>, " + Environment.NewLine);
|
||||
lsl.Append(" PSYS_PART_MAX_AGE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartMaxAge) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_MAX_AGE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.MaxAge) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_ACCEL, " + exportPrim.ParticleSys.PartAcceleration.ToString() + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_BURST_PART_COUNT, " + String.Format("{0:0}", exportPrim.ParticleSys.BurstPartCount) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_BURST_RADIUS, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstRadius) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_BURST_RATE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstRate) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_BURST_SPEED_MIN, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstSpeedMin) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_BURST_SPEED_MAX, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstSpeedMax) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_INNERANGLE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.InnerAngle) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_OUTERANGLE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.OuterAngle) + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_OMEGA, " + exportPrim.ParticleSys.AngularVelocity.ToString() + "," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_TEXTURE, (key)\"" + exportPrim.ParticleSys.Texture.ToString() + "\"," + Environment.NewLine);
|
||||
lsl.Append(" PSYS_SRC_TARGET_KEY, (key)\"" + exportPrim.ParticleSys.Target.ToString() + "\"" + Environment.NewLine);
|
||||
|
||||
lsl.Append(" ]);" + Environment.NewLine);
|
||||
lsl.Append(" }" + Environment.NewLine);
|
||||
lsl.Append("}" + Environment.NewLine);
|
||||
|
||||
#endregion Particle System to LSL
|
||||
|
||||
return lsl.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Prim " + exportPrim.LocalID + " does not have a particle system";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "Couldn't find prim " + id.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
|
||||
namespace OpenMetaverse.TestClient
|
||||
{
|
||||
public class FindObjectsCommand : Command
|
||||
{
|
||||
Dictionary<UUID, Primitive> PrimsWaiting = new Dictionary<UUID, Primitive>();
|
||||
AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false);
|
||||
|
||||
public FindObjectsCommand(TestClient testClient)
|
||||
{
|
||||
testClient.Objects.ObjectProperties += new EventHandler<ObjectPropertiesEventArgs>(Objects_OnObjectProperties);
|
||||
|
||||
Name = "findobjects";
|
||||
Description = "Finds all objects, which name contains search-string. " +
|
||||
"Usage: findobjects [radius] <search-string>";
|
||||
Category = CommandCategory.Objects;
|
||||
}
|
||||
|
||||
public override string Execute(string[] args, UUID fromAgentID)
|
||||
{
|
||||
// *** parse arguments ***
|
||||
if ((args.Length < 1) || (args.Length > 2))
|
||||
return "Usage: findobjects [radius] <search-string>";
|
||||
float radius = float.Parse(args[0]);
|
||||
string searchString = (args.Length > 1) ? args[1] : String.Empty;
|
||||
|
||||
// *** get current location ***
|
||||
Vector3 location = Client.Self.SimPosition;
|
||||
|
||||
// *** find all objects in radius ***
|
||||
List<Primitive> prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(
|
||||
delegate(Primitive prim)
|
||||
{
|
||||
Vector3 pos = prim.Position;
|
||||
return ((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius));
|
||||
}
|
||||
);
|
||||
|
||||
// *** request properties of these objects ***
|
||||
bool complete = RequestObjectProperties(prims, 250);
|
||||
|
||||
foreach (Primitive p in prims)
|
||||
{
|
||||
string name = p.Properties != null ? p.Properties.Name : null;
|
||||
if (String.IsNullOrEmpty(searchString) || ((name != null) && (name.Contains(searchString))))
|
||||
Console.WriteLine(String.Format("Object '{0}': {1}", name, p.ID.ToString()));
|
||||
}
|
||||
|
||||
if (!complete)
|
||||
{
|
||||
Console.WriteLine("Warning: Unable to retrieve full properties for:");
|
||||
foreach (UUID uuid in PrimsWaiting.Keys)
|
||||
Console.WriteLine(uuid);
|
||||
}
|
||||
|
||||
return "Done searching";
|
||||
}
|
||||
|
||||
private bool RequestObjectProperties(List<Primitive> objects, int msPerRequest)
|
||||
{
|
||||
// Create an array of the local IDs of all the prims we are requesting properties for
|
||||
uint[] localids = new uint[objects.Count];
|
||||
|
||||
lock (PrimsWaiting)
|
||||
{
|
||||
PrimsWaiting.Clear();
|
||||
|
||||
for (int i = 0; i < objects.Count; ++i)
|
||||
{
|
||||
localids[i] = objects[i].LocalID;
|
||||
PrimsWaiting.Add(objects[i].ID, objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Client.Objects.SelectObjects(Client.Network.CurrentSim, localids);
|
||||
|
||||
return AllPropertiesReceived.WaitOne(2000 + msPerRequest * objects.Count, false);
|
||||
}
|
||||
|
||||
void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e)
|
||||
{
|
||||
lock (PrimsWaiting)
|
||||
{
|
||||
Primitive prim;
|
||||
if (PrimsWaiting.TryGetValue(e.Properties.ObjectID, out prim))
|
||||
{
|
||||
prim.Properties = e.Properties;
|
||||
}
|
||||
PrimsWaiting.Remove(e.Properties.ObjectID);
|
||||
|
||||
if (PrimsWaiting.Count == 0)
|
||||
AllPropertiesReceived.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user