/*
* Copyright (c) Contributors, http://www.openmetaverse.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* 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.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSim Project 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 DEVELOPERS ``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 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.
*
*/
/* Original code: Tedd Hansen */
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Reflection;
using OpenSim.Region.Environment.Scenes;
using OpenSim.Region.Environment.Scenes.Scripting;
namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
///
/// Loads scripts
/// Compiles them if necessary
/// Execute functions for EventQueueManager
///
public class ScriptManager
{
private ScriptEngine m_scriptEngine;
public ScriptManager(ScriptEngine scriptEngine)
{
m_scriptEngine = scriptEngine;
m_scriptEngine.Log.Verbose("ScriptEngine", "ScriptManager Start");
}
// Object>
internal Dictionary> Scripts = new Dictionary>();
public Scene World
{
get
{
return m_scriptEngine.World;
}
}
internal Dictionary.KeyCollection GetScriptKeys(string ObjectID)
{
if (Scripts.ContainsKey(ObjectID) == false)
return null;
Dictionary Obj;
Scripts.TryGetValue(ObjectID, out Obj);
return Obj.Keys;
}
internal OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass GetScript(string ObjectID, string ScriptID)
{
if (Scripts.ContainsKey(ObjectID) == false)
return null;
Dictionary Obj;
Scripts.TryGetValue(ObjectID, out Obj);
if (Obj.ContainsKey(ScriptID) == false)
return null;
// Get script
OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass Script;
Obj.TryGetValue(ScriptID, out Script);
return Script;
}
internal void SetScript(string ObjectID, string ScriptID, OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass Script)
{
// Create object if it doesn't exist
if (Scripts.ContainsKey(ObjectID) == false)
{
Scripts.Add(ObjectID, new Dictionary());
}
// Delete script if it exists
Dictionary Obj;
Scripts.TryGetValue(ObjectID, out Obj);
if (Obj.ContainsKey(ScriptID) == true)
Obj.Remove(ScriptID);
// Add to object
Obj.Add(ScriptID, Script);
}
///
/// Fetches, loads and hooks up a script to an objects events
///
///
///
public void StartScript(string ScriptID, string ObjectID)
{
m_scriptEngine.Log.Verbose("ScriptEngine", "ScriptManager StartScript: ScriptID: " + ScriptID + ", ObjectID: " + ObjectID);
// We will initialize and start the script.
// It will be up to the script itself to hook up the correct events.
string FileName = "";
try
{
// * Fetch script from server
// DEBUG - ScriptID is an actual filename during debug
// (therefore we can also check type by looking at extension)
FileName = ScriptID;
// * Does script need compile? Send it to LSL compiler first. (TODO: Use (and clean) compiler cache)
//myScriptEngine.m_logger.Verbose("ScriptEngine", "ScriptManager Script extension: " + System.IO.Path.GetExtension(FileName).ToLower());
switch (System.IO.Path.GetExtension(FileName).ToLower())
{
case ".txt":
case ".lsl":
case ".cs":
m_scriptEngine.Log.Verbose("ScriptEngine", "ScriptManager Script is CS/LSL, compiling to .Net Assembly");
// Create a new instance of the compiler (currently we don't want reuse)
OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler LSLCompiler = new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler();
// Compile
FileName = LSLCompiler.Compile(FileName);
break;
default:
throw new Exception("Unknown script type.");
}
m_scriptEngine.Log.Verbose("ScriptEngine", "Compilation done");
// * Insert yield into code
FileName = ProcessYield(FileName);
// * Find next available AppDomain to put it in
AppDomain FreeAppDomain = GetFreeAppDomain();
// * Load and start script, for now with dummy host
//OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSO.LSL_BaseClass Script = LoadAndInitAssembly(FreeAppDomain, FileName);
IScriptHost host = new NullScriptHost();
IScriptHost root = host;
OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass Script = LoadAndInitAssembly(FreeAppDomain, FileName, host, root );
string FullScriptID = ScriptID + "." + ObjectID;
// Add it to our temporary active script keeper
//Scripts.Add(FullScriptID, Script);
SetScript(ObjectID, ScriptID, Script);
// We need to give (untrusted) assembly a private instance of BuiltIns
// this private copy will contain Read-Only FullScriptID so that it can bring that on to the server whenever needed.
//OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL_BuiltIn_Commands_Interface LSLB = new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL_BuiltIn_Commands_TestImplementation(FullScriptID);
// Start the script - giving it BuiltIns
//myScriptEngine.m_logger.Verbose("ScriptEngine", "ScriptManager initializing script, handing over private builtin command interface");
Script.Start( ScriptID );
}
catch (Exception e)
{
m_scriptEngine.Log.Error("ScriptEngine", "Exception loading script \"" + FileName + "\": " + e.ToString());
}
}
private string ProcessYield(string FileName)
{
// TODO: Create a new assembly and copy old but insert Yield Code
//return TempDotNetMicroThreadingCodeInjector.TestFix(FileName);
return FileName;
}
private AppDomain GetFreeAppDomain()
{
// TODO: Find an available AppDomain - if none, create one and add default security
return Thread.GetDomain();
}
///
/// Does actual loading and initialization of script Assembly
///
/// AppDomain to load script into
/// FileName of script assembly (.dll)
///
private OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass LoadAndInitAssembly(AppDomain FreeAppDomain, string FileName, IScriptHost host, IScriptHost root)
{
//myScriptEngine.m_logger.Verbose("ScriptEngine", "ScriptManager Loading Assembly " + FileName);
// Load .Net Assembly (.dll)
// Initialize and return it
// TODO: Add error handling
// Script might not follow our rules since users can upload -anything-
Assembly a;
//try
//{
// Load to default appdomain (temporary)
a = Assembly.LoadFrom(FileName);
// Load to specified appdomain
// TODO: Insert security
//a = FreeAppDomain.Load(FileName);
//}
//catch (Exception e)
//{
//}
//foreach (Type _t in a.GetTypes())
//{
// Console.WriteLine("Type: " + _t.ToString());
//}
Type t;
//try
//{
t = a.GetType("SecondLife.Script", true);
//}
//catch (Exception e)
//{
//}
// Create constructor arguments
object[] args = new object[]
{
this,
host,
root
};
return (OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass)Activator.CreateInstance(t, args );
}
internal void ExecuteFunction(string ObjectID, string ScriptID, string FunctionName, object[] args)
{
m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function ObjectID: " + ObjectID + ", ScriptID: " + ScriptID + ", FunctionName: " + FunctionName);
OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass Script = m_scriptEngine.myScriptManager.GetScript(ObjectID, ScriptID);
Type type = Script.GetType();
m_scriptEngine.Log.Verbose("ScriptEngine", "Invoke: \"" + Script.State + "_event_" + FunctionName + "\"");
try
{
type.InvokeMember(Script.State + "_event_" + FunctionName, BindingFlags.InvokeMethod, null, Script, args);
}
catch (Exception e)
{
m_scriptEngine.Log.Error("ScriptEngine", "Exception attempting to executing script function: " + e.ToString());
}
//foreach (MemberInfo mi in type.GetMembers())
//{
// Common.SendToDebug("Member found: " + mi.ToString());
//}
}
public string RegionName
{
get
{
return World.RegionInfo.RegionName;
}
}
}
}