Shortnote

The client is a bot that can be run on the command-line and is controlled by the controller script from in-world. The client requires a configuration that is stored in Simscape.config in the same directory as the executable.

A sample configuration file looks like this:

firstname:Simscape
lastname:Sextant
password:$1$a21bb8e950fda536faa10fc753a68a3d
grid:https://login.agni.lindenlab.com/cgi-bin/login.cgi
secret:PASSWORD
master:52ec55a8-5792-4ec6-abc5-b92b6ae37c59

Where the following parameters are:

  • firstname - is the first name of the user connecting to the server.
  • lastname - is the last name of the user connecting to the server.
  • grid - is the login URI for the grid, this can be left to the default for Second Life.
  • secret - this is a shared secret between Simscape and the controller script.
  • master - is the UUID of the agent that has superuser privileges for the bot.

For Second Life, everything aside from the grid must be changed to your set-up.

Code

Simscape.cs
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2012 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Threading;
using System.Collections.Generic;
using OpenMetaverse;
using System.Linq;
 
namespace Simscape {
  internal class Simscape {
    private static readonly GridClient Client = new GridClient();
    private static string _firstName = string.Empty;
    private static string _lastName = string.Empty;
    private static string _password = string.Empty;
    private static List < UUID > _masters = new List < UUID > ();
    private static readonly Thread MainConnectionThread = new Thread(MainConnection);
    private static readonly Thread StatisticsThread = new Thread(SendStatistics);
    private static Dictionary < char, bool > StabilizeChecks = new Dictionary < char, bool > () {
      {
        'l', false
      }, {
        'e', false
      }, {
        's', false
      }, {
        'a', false
      }
    };
    private static List < string > AuthAgents = new List < string > ();
    private static volatile bool _run = true;
    private static volatile bool _runStatistics = true;
    private static volatile string _url = string.Empty;
 
    public static void Main() {
 
      Settings.LOG_LEVEL = Helpers.LogLevel.None;
      Client.Settings.STORE_LAND_PATCHES = true;
      Client.Settings.ALWAYS_DECODE_OBJECTS = true;
      Client.Settings.ALWAYS_REQUEST_OBJECTS = true;
      Client.Settings.SEND_AGENT_UPDATES = true;
      Client.Settings.USE_ASSET_CACHE = false;
      Client.Settings.FETCH_MISSING_INVENTORY = true;
 
      Client.Network.LoginProgress += HandleLoginProgress;
      Client.Appearance.AppearanceSet += HandleAppearanceSet;
      Client.Network.SimConnected += HandleSimulatorConnected;
      Client.Network.Disconnected += HandleDisconnected;
      Client.Network.SimDisconnected += HandleSimulatorDisconnected;
      Client.Network.EventQueueRunning += HandleEventQueueRunning;
 
      Client.Friends.FriendshipOffered += HandleFriendshipOffered;
      Client.Self.IM += HandleSelfIM;
 
      try {
        foreach(string cfg in File.ReadLines("Simscape.config")) {
          string[] data = cfg.Split(new char[] {
            ':'
          }, 2);
          switch (data[0]) {
          case "firstname":
            _firstName = data[1];
            break;
          case "lastname":
            _lastName = data[1];
            break;
          case "password":
            _password = data[1];
            break;
          case "grid":
            Client.Settings.LOGIN_SERVER = data[1];
            break;
            // auth-secrets and masters are read dynamically on request.
          case "master":
            break;
          case "secret":
            break;
          default:
            Console.WriteLine("[Simscape] : Garbled line: " + cfg + ", the program will now terminate.");
            System.Environment.Exit(1);
            break;
          }
        }
      } catch (FileNotFoundException) {
        Console.WriteLine("[Simscape] : Could not read configuration file.");
        System.Environment.Exit(1);
      }
 
      new List < int > (new int[Console.WindowWidth]).ForEach(i => Console.Write("-"));
      MainConnectionThread.Start();
    }
 
    private static void HandleDisconnected(object sender, DisconnectedEventArgs e) {
      Console.Write("\n[Simscape] : Disconnected...");
      if (!StabilizeChecks.ContainsKey('l')) {
        StabilizeChecks.Add('l', false);
        return;
      }
      StabilizeChecks['l'] = false;
    }
 
    private static void HandleEventQueueRunning(object sender, EventQueueRunningEventArgs e) {
      Console.Write("\n[Simscape] : Event queue started...");
      if (!StabilizeChecks.ContainsKey('e')) {
        StabilizeChecks.Add('e', true);
        return;
      }
      StabilizeChecks['e'] = true;
    }
 
    private static void HandleSimulatorConnected(object sender, SimConnectedEventArgs e) {
      Console.Write("\n[Simscape] : Simulator connected...");
      if (!StabilizeChecks.ContainsKey('s')) {
        StabilizeChecks.Add('s', true);
        return;
      }
      StabilizeChecks['s'] = true;
    }
 
    private static void HandleSimulatorDisconnected(object sender, SimDisconnectedEventArgs e) {
      if (Client.Network.Simulators.Count >= 1) return;
      Console.Write("\n[Simscape] : Simulators disconnected...");
      if (!StabilizeChecks.ContainsKey('s')) {
        StabilizeChecks.Add('s', false);
        return;
      }
      StabilizeChecks['s'] = false;
    }
 
    private static void HandleAppearanceSet(object sender, AppearanceSetEventArgs e) {
      if (e.Success) {
        Console.Write("\n[Simscape] : Appearance set...");
        if (!StabilizeChecks.ContainsKey('a')) {
          StabilizeChecks.Add('a', true);
          return;
        }
        StabilizeChecks['a'] = true;
        return;
      }
      Console.Write("\n[Simscape] : Failed to set appearance...");
      if (!StabilizeChecks.ContainsKey('a')) {
        StabilizeChecks.Add('a', false);
        return;
      }
      StabilizeChecks['a'] = false;
    }
 
    private static void HandleLoginProgress(object sender, LoginProgressEventArgs e) {
      switch (e.Status) {
      case LoginStatus.Success:
        Console.Write("\n[Simscape] : Login ok...");
        StabilizeChecks['l'] = true;
        break;
      case LoginStatus.Failed:
        Console.Write("\n[Simscape] : Failed Login...");
        if (!StabilizeChecks.ContainsKey('l')) {
          StabilizeChecks.Add('l', false);
          break;
        }
        StabilizeChecks['l'] = false;
        break;
      }
    }
 
    private static void HandleFriendshipOffered(object sender, FriendshipOfferedEventArgs e) {
      if (!_masters.Contains(e.AgentID)) return;
      Console.Write("\n[Simscape] : Accepting friendship with " + e.AgentName + ".");
      Client.Friends.AcceptFriendship(e.AgentID, e.SessionID);
    }
 
    private static void MainConnection() {
      StabilizeChecks = new Dictionary < char, bool > ();
      Client.Network.BeginLogin(new LoginParams(Client, _firstName, _lastName, _password, "[WaS] Corrade", "1.0"));
      Console.Write("\n[Corrade] : Stabilizing, please wait...");
      do {
        Console.Write(".");
        Thread.Sleep(1000);
      } while (StabilizeChecks.Count != 4 && !StabilizeChecks.ContainsValue(false));
      if (!StabilizeChecks.ContainsValue(false)) Console.Write("\n[Corrade] : Fully connected and accepting commands.");
      do {
        Thread.Sleep(1000);
        if (StabilizeChecks.ContainsValue(false)) {
          Console.Write("\n[Corrade] : Disconnected, shutting down...");
          _run = false;
        }
      }
      while (_run);
      Console.Write("\n[Corrade] : All operations completed. Quitting.");
      new List < int > (new int[Console.WindowWidth]).ForEach(i => Console.Write("-"));
      Client.Network.Logout();
      Client.Network.Shutdown(NetworkManager.DisconnectType.ClientInitiated);
    }
 
    private static void HandleSelfIM(object sender, InstantMessageEventArgs e) {
      if (e.IM.Dialog.Equals(InstantMessageDialog.MessageBox)) {
        Console.Write("\n[Simscape] : [Server Message] : " + e.IM.Message.Replace(System.Environment.NewLine, " "));
        return;
      }
      if (e.IM.Dialog.Equals(InstantMessageDialog.RequestTeleport)) {
        try {
          foreach(string cfg in File.ReadLines("Simscape.config")) {
            string[] data = cfg.Split(new char[] {
              ':'
            }, 3);
            switch (data[0]) {
            case "master":
              var master = UUID.Zero;
              if (!UUID.TryParse(data[1], out master)) continue;
              if (_masters.Contains(master)) continue;
              _masters.Add(master);
              break;
            }
          }
        } catch (FileNotFoundException) {
          Console.Write("\n[Simscape] : Could not read configuration file.");
          System.Environment.Exit(1);
        }
        if (!_masters.Contains(e.IM.FromAgentID)) return;
        Console.Write("\n[Simscape] : Accepting teleport lure.");
        Client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true);
        return;
      }
      // Ignore group chat.
      if (e.IM.GroupIM) return;
      var msg = e.IM.Message.Split(new char[] {
        ' '
      });
      if (!msg[0].Equals("auth")) goto NoAuth;
      try {
        foreach(string cfg in File.ReadLines("Simscape.config")) {
          string[] data = cfg.Split(new char[] {
            ':'
          }, 3);
          switch (data[0]) {
          case "secret":
            if (!data[1].Equals(msg[1])) break;
            if (!AuthAgents.Contains(e.IM.FromAgentName)) AuthAgents.Add(e.IM.FromAgentName);
            Console.Write("\n[Simscape] : Authenticated: " + e.IM.FromAgentName + ".");
            break;
          }
        }
      } catch (FileNotFoundException) {
        Console.Write("\n[Simscape] : Could not read configuration file.");
        System.Environment.Exit(1);
      }
      return;
      NoAuth: foreach(var agent in AuthAgents) {
        if (!agent.Equals(e.IM.FromAgentID)) continue;
        goto Command;
      }
      return;
      Command: if (msg[0].Equals("o")) goto TeleportCommand;
      if (msg[0].Equals("r")) goto RestartCommand;
      return;
 
      TeleportCommand:
      // Terminate the statistics thread.
      if (StatisticsThread.ThreadState.Equals(ThreadState.Running)) {
        _runStatistics = false;
        StatisticsThread.Join();
      }
      // Get the URL.
      _url = msg[1];
      // Pop the command off the array.
      var cmdArgs = new List < string > (msg);
      cmdArgs.RemoveAt(0);
      // Pop the URL off the array.
      cmdArgs.RemoveAt(0);
      // Get the rest of the command.
      var _newSimulatorName = String.Join(" ", cmdArgs.ToArray());
      if (_newSimulatorName.Equals(Client.Network.CurrentSim.Name)) {
        Console.Write("\n[Simscape] : Already on simulator " + _newSimulatorName + ".");
        HTTPRequest(_url, "POST", "Teleport:FAILED");
        return;
      }
      var TeleportEvent = new ManualResetEvent(false);
      Client.Self.TeleportProgress += delegate(object tps, TeleportEventArgs et) {
        if (et.Status.Equals(TeleportStatus.Finished)) {
          TeleportEvent.Set();
        }
      };
      Client.Self.Fly(true);
      Console.Write("\n[Simscape] : Teleporting to " + _newSimulatorName + ".");
      Client.Self.Teleport(_newSimulatorName, new Vector3(128, 128, 30000));
      if (!TeleportEvent.WaitOne(10000, false)) {
        Console.Write("\n[Simscape] : Teleport failed to " + _newSimulatorName + ".");
        HTTPRequest(_url, "POST", "Teleport:FAILED");
        TeleportEvent.Reset();
        return;
      }
      TeleportEvent.Reset();
      Console.Write("\n[Simscape] : Teleport to " + _newSimulatorName + " successful.");
      HTTPRequest(_url, "POST", "Teleport:SUCCESS");
 
      _runStatistics = true;
      StatisticsThread.Start();
      return;
 
      RestartCommand: return;
 
    }
 
    private static void SendStatistics() {
      do {
        HTTPRequest(_url, "PUT", String.Format("[{0}]|Active scripts: {1}|Agents: {2}|Child agents: {3}|Dilation: {4}|FPS: {5}|Objects: {6}|Physics FPS: {7}|Physics time: {8}|Scripted Objects: {9}|CPURatio: {10}\n", Client.Network.CurrentSim.Name, Client.Network.CurrentSim.Stats.ActiveScripts, Client.Network.CurrentSim.Stats.Agents, Client.Network.CurrentSim.Stats.ChildAgents, Client.Network.CurrentSim.Stats.Dilation, Client.Network.CurrentSim.Stats.FPS, Client.Network.CurrentSim.Stats.Objects, Client.Network.CurrentSim.Stats.PhysicsFPS, Client.Network.CurrentSim.Stats.PhysicsTime, Client.Network.CurrentSim.Stats.ScriptedObjects, Client.Network.CurrentSim.CPURatio));
        Thread.Sleep(1000);
      } while (_runStatistics);
    }
 
    private static HttpWebResponse HTTPRequest(string url, string method, string data) {
      try {
        byte[] byteArray = Encoding.UTF8.GetBytes(data);
        WebRequest request = WebRequest.Create(url);
        request.Method = method;
        request.Timeout = 1000;
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = byteArray.Length;
        Stream dataStream = request.GetRequestStream();
        dataStream.Write(byteArray, 0, byteArray.Length);
        dataStream.Close();
        HttpWebResponse response = (HttpWebResponse) request.GetResponse();
        return response;
      } catch (WebException e) {
        return (HttpWebResponse) e.Response;
      }
    }
  }
}

secondlife/scripted_agents/simscape/client.txt ยท Last modified: 2022/11/24 07:45 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.