muddesigner/MudEngine/Scripting/ScriptEngine.cs
Scionwest_cp a347607337 Mud Designer:
- Added new Project. Mud Designer project will include the GUI elements needed for graphically building a MUD. Due to Environment creation being finalized, work on a GUI based Environment creation can start.

Mud Engine:
 - Objects no longer require a path to be supplied when calling Object.Save()
 - EditRealm command now edits senses.
 - EditRoom now fully supports creating doorways, however editing existing doorways and linking to existing rooms is not implemented. This command only supports creating new doorways for non-existing Rooms (Rooms are generated as needed)
 - EditZone Now fully supports senses and implemented.
 - Game now supports loading of .ini files when calling Game.Load()
 - All objects now include a SavePath properties. Override this to supply a path for where the object needs to be saved. All Environment and BaseCharacter objects override the BaseObject.SavePath to save into ActiveGame.DataPaths.Environemnts and Players respectively.
 - ObjectCollection now instanced during ScriptEngine initialization to prevent exceptions during runtime.
 - Create command no longer converts all names to lower case.
 - Updated the Walk command to execute the Look command in a safe manor without injecting a command into the player Telnet console.
2010-09-26 08:00:34 -07:00

380 lines
14 KiB
C#

//Microsoft .NET Framework
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
#if !MOBILE
using Microsoft.CSharp;
#endif
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Reflection;
using MudEngine.FileSystem;
using MudEngine.GameObjects;
using MudEngine.GameObjects.Characters;
using MudEngine.GameManagement;
namespace MudEngine.Scripting
{
public class ScriptEngine
{
public enum ScriptTypes
{
Assembly,
SourceFiles,
Both
}
/// <summary>
/// Path to the the script files directory
/// </summary>
public String ScriptPath
{
get
{
return _ScriptPath;
}
set
{
_ScriptPath = Path.Combine(FileManager.GetDataPath(SaveDataTypes.Root), value);
}
}
String _ScriptPath;
public String InstallPath { get; private set; }
public GameObjectCollection ObjectCollection { get; private set; }
/// <summary>
/// Collection of currently loaded objects created from compiled scripts
/// </summary>
public List<GameObject> GameObjects { get; private set; }
/// <summary>
/// Collection of currently loaded game commecnts that can be used. These must be compiled scripts inheriting from IGameCommand
/// </summary>
public List<IGameCommand> GameCommands { get; private set; }
/// <summary>
/// File Extension for the scripts
/// </summary>
public String ScriptExtension { get; set; }
/// <summary>
/// Error Messages logged during script compilation
/// </summary>
public String ErrorMessage
{
get
{
String errorMessages = "Script Compilation Failed!\n";
//Construct our error message.
foreach (String error in _ErrorMessages)
errorMessages += error + "\n";
return errorMessages;
}
private set
{
_ErrorMessages = new String[] { value };
}
}
internal ScriptTypes ScriptType { get; set; }
private Assembly _ScriptAssembly;
private List<Assembly> _AssemblyCollection;
private String[] _ErrorMessages;
private String _SettingsFile;
Game _Game;
public ScriptEngine(Game game) : this(game, ScriptTypes.Assembly)
{
_Game = game;
}
/// <summary>
/// Instances a new copy of the script engine
/// </summary>
/// <param name="scriptTypes">Tells the engine what kind of scripts will be loaded. Source File or assembly based.</param>
public ScriptEngine(Game game, ScriptTypes scriptTypes)
{
//Initialize our engine fields
_SettingsFile = "Settings.ini";
ScriptExtension = FileManager.GetData(_SettingsFile, "ScriptExtension");
if (String.IsNullOrEmpty(ScriptExtension))
ScriptExtension = ".cs";
//Get our current install path
ScriptPath = FileManager.GetData(_SettingsFile, "ScriptPath");
if (String.IsNullOrEmpty(ScriptPath))
ScriptPath = "Scripts";
InstallPath = Environment.CurrentDirectory;
GameObjects = new List<GameObject>();
_AssemblyCollection = new List<Assembly>();
ObjectCollection = new GameObjectCollection();
ScriptType = scriptTypes;
_Game = game;
}
/// <summary>
/// Compiles a collection of scripts stored in ScriptEngine.ScriptPath. Not supported on XBox360.
/// </summary>
/// <returns></returns>
public Boolean CompileScripts()
{
#if !MOBILE
//Ensure the script path exists.
if (!System.IO.Directory.Exists(ScriptPath))
{
ErrorMessage = "Invalid Script path supplied.";
return false;
}
//Build an array of scripts
String[] scripts = System.IO.Directory.GetFiles(ScriptPath, "*" + ScriptExtension, System.IO.SearchOption.AllDirectories);
//Prepare the scripts. MUD Scripts are wrote without defining a namespace
if (Directory.Exists("temp"))
Directory.Delete("temp", true);
Directory.CreateDirectory("temp");
//Setup the additional sourcecode that's needed in the script.
String[] usingStatements = new String[]
{
"using System;",
"using System.Collections.Generic;",
"using System.Text;",
"using System.Linq;",
"using MudEngine.GameObjects;",
"using MudEngine.GameObjects.Characters;",
"using MudEngine.GameObjects.Environment;",
"using MudEngine.GameObjects.Items;",
"using MudEngine.GameManagement;",
"using MudEngine.FileSystem;",
"using MudEngine.Scripting;"
};
foreach (String script in scripts)
{
String tempPath = "temp";
String source = "\nnamespace MudScripts{\n}";
FileStream fr = new FileStream(script, FileMode.Open, FileAccess.Read, FileShare.None);
FileStream fw = new FileStream(Path.Combine(tempPath, Path.GetFileName(script)), FileMode.Create, FileAccess.Write);
StreamWriter sw = new StreamWriter(fw, System.Text.Encoding.Default);
StreamReader sr = new StreamReader(fr, System.Text.Encoding.Default);
String content = sr.ReadToEnd();
foreach (String statement in usingStatements)
source = source.Insert(0, statement);
source = source.Insert(source.Length - 1, content);
sw.Write(source);
sr.Close();
sw.Flush();
sw.Close();
}
String oldPath = ScriptPath;
ScriptPath = "temp";
//Prepare the compiler.
Dictionary<String, String> providerOptions = new Dictionary<String,String>();
providerOptions.Add("CompilerVersion", "v4.0");
String[] referencedAssemblies = new String[]
{
"mscorlib.dll",
"System.dll",
"System.Core.dll",
"MudEngine.dll"
};
CompilerParameters param = new CompilerParameters(referencedAssemblies);
param.GenerateExecutable = false;
param.GenerateInMemory = true;
if (!param.GenerateInMemory)
param.OutputAssembly = Path.Combine(oldPath, "Scripts.dll");
param.IncludeDebugInformation = false;
param.TreatWarningsAsErrors = true;
//Compile the scripts with the C# CodeProvider
CSharpCodeProvider codeProvider = new CSharpCodeProvider(providerOptions);
//codeProvider.LanguageOptions = LanguageOptions.CaseInsensitive;
CompilerResults results = new CompilerResults(new TempFileCollection());
scripts = Directory.GetFiles(ScriptPath, "*" + ScriptExtension, SearchOption.AllDirectories);
results = codeProvider.CompileAssemblyFromFile(param, scripts);
//Delete the temp folder
Directory.Delete("temp", true);
ScriptPath = oldPath;
//if we encountered errors we need to log them to our ErrorMessages property
if (results.Errors.Count >= 1)
{
List<String> errorCollection = new List<String>();
foreach (CompilerError error in results.Errors)
{
String prefix = "Error: ";
if (error.IsWarning)
prefix = "Warning: ";
errorCollection.Add(prefix + error.FileName + "(" + error.Line + ") - " + error.ErrorText);
_ErrorMessages = errorCollection.ToArray();
}
return false;
}
else
{
_ScriptAssembly = results.CompiledAssembly;
return true;
}
#endif
}
/// <summary>
/// Initializes the script engine, loading the compiled scripts into memory
/// </summary>
/// <param name="scriptAssembly"></param>
public void Initialize()
{
if ((ScriptType == ScriptTypes.Assembly) || (ScriptType == ScriptTypes.Both))
{
Log.Write("Loading Assembly based Scripts...");
InitializeAssembly();
}
if ((ScriptType == ScriptTypes.SourceFiles) || (ScriptType == ScriptTypes.Both))
{
Log.Write("Loading Source File based Scripts...");
InitializeSourceFiles();
}
foreach (Assembly assembly in _AssemblyCollection)
{
Log.Write("Checking " + Path.GetFileName(assembly.Location) + " for scripts...");
foreach (Type t in assembly.GetTypes())
{
if (t.BaseType == null)
continue;
if (t.BaseType.Name == "BaseObject")
{
GameObjects.Add(new GameObject(Activator.CreateInstance(t, new object[] {_Game}), t.Name));
Log.Write(t.Name + " script loaded.");
continue;
}
else if (t.BaseType.Name == "BaseCharacter")
{
GameObject obj = new GameObject(Activator.CreateInstance(t, new object[] {_Game}), t.Name);
GameObjects.Add(obj);
//obj.GetProperty().CurrentRoom = _Game.InitialRealm.InitialZone.InitialRoom;
Log.Write(t.Name + " script loaded.");
continue;
}
else if (t.BaseType.Name == "Game")
{
GameObject obj = new GameObject(Activator.CreateInstance(t, null), t.Name);
GameObjects.Add(obj);
}
}
//Lastly, send this assembly off to the CommandEngine so we can load commands from it for use as well.
CommandEngine.LoadCommandLibrary(assembly);
}
_AssemblyCollection.Clear();
}
private void InitializeAssembly()
{
if (!Directory.Exists(ScriptPath))
{
Log.Write("Supplied script path does not exist! No scripts loaded.");
return;
}
String[] libraries = Directory.GetFiles(ScriptPath, "*.dll", SearchOption.AllDirectories);
if (libraries.Length == 0)
{
Log.Write("No possible script libraries found.");
return;
}
foreach (String library in libraries)
{
Boolean isOK = true;
foreach (Assembly assembly in _AssemblyCollection)
{
if (assembly.ManifestModule.ScopeName == Path.GetFileName(library))
{
isOK = false;
break;
}
}
if (!isOK)
continue;
Log.Write("Found possible script libary: " + Path.GetFileName(library));
_AssemblyCollection.Add(Assembly.LoadFile(library));
}
_AssemblyCollection.Add(Assembly.GetExecutingAssembly());
}
private void InitializeSourceFiles()
{
if (!Directory.Exists(ScriptPath))
Directory.CreateDirectory(ScriptPath);
String[] scripts = Directory.GetFiles(ScriptPath, "*.cs", SearchOption.AllDirectories);
if (scripts.Length == 0)
{
Log.Write("No un-compiled scripts located!");
return;
}
if (!CompileScripts())
{
Log.Write("Error Compiling Scripts:");
foreach (String error in _ErrorMessages)
{
Log.Write("Error: " + error);
}
}
else
_AssemblyCollection.Add(_ScriptAssembly);
}
public GameObject GetObject(String objectName)
{
IEnumerable<GameObject> objectQuery =
from gameObject in ObjectCollection._GameObjects
where gameObject.Name == objectName
select gameObject;
foreach (GameObject gameObject in objectQuery)
{
if (gameObject.Name == objectName)
return gameObject;
}
return null;
}
public GameObject GetObjectOf(String baseTypeName)
{
foreach (GameObject obj in GameObjects)
{
if (obj.Instance.GetType().BaseType.Name == baseTypeName)
return obj;
}
return null;
}
}
}