C# Interpreter Console for Unity 3D

CSI is a simple C# interpreter that was originally developed by Steve Donovan and it works similar to the more recently developed C# interactive shell of Mono, named CsharpRepl. For a while I’ve been making some improvements to CSI and recently developed a plug-in/script version of it for Unity, the 3D game-development environment, which is compatible with the older version of Mono that Unity makes use of. The plugin allows one to execute C# code in an interactive console while the game is running, which can assist with debugging and be used to explore the runtime environment. The software is known to work on Unity 2.6.1 in both the Editor application and Standalone Player on Windows, but it might also work on the Mac OS. It also works on Unity 3.0.0 and 3.1.0 for PC (as of CSI version 0.8.24.3), as long as your project’s “API Compatibility Level” is set to “.NET 2.0” (not the default “.NET 2.0 Subset”).

Download and Install the Plugin

Files of CSI Plugin shown in Project window of Unity EditorThe CSI plug-in is available as a free download and released as open-source software under the MIT License. It is distributed in multiple formats: a ZIP file, a Unity Package and a Git repository, all containing the four files that need to be installed somewhere under the Assets directory-structure of your Unity project. If you prefer the packaged version, import the files into your project through the Unity Editor’s “Assets | Import Package...” menu. Alternatively, you can decompress the ZIP file’s content into an assets subdirectory, for example “Assets\Plugins\CSI” underneath you project’s root folder. After installation, you should see something in your Project window that is similar to the image shown; it represents the four files of the plug-in:

  • CSharpInterpreter.cs
  • CSharpInterpreter_Include.txt
  • interpreter.cs
  • prepro.cs

The first file, CSharpInterpreter.cs, is the primary script that implements the hosting environment for the C# Interpreter, which is a Component that can be attached to a GameObject in Unity 3D. For example, to get the interpreter GUI to show up at runtime, use the “GameObject | Create Empty” menu item in the Unity Editor, which would result in a new GameObject being shown in your project’s Hierarchy window; then drag-and-drop the CSharpInterpreter script from the Project window onto your new GameObject to attach the script to the object. You can now “Play” the scene and should see the interactive console’s user interface (discussed in more detail in the following section).

By default, the CSharpInterpreter_Include.txt file gets included during startup to configure the interpreter environment. It specifies additional namespaces and assembly references that must be included, as well as macros (such as print) that can save you some typing. You can edit this text file to tweak the interpreter environment to your liking – each line in this file is effectively processed as if it were provided manually as input to the interpreter, one step at a time.

The remaining two files, interpreter.cs and prepro.cs, contain the underlying CSI interpreter and preprocessor code that provide support for the CSI game component. You would rarely need to work with them directly.

Runtime User Interface

Once you have the CSI plug-in for Unity installed and running, its runtime user-interface will display as overlay controls shown on top of your game scene in the background – for example, see the screenshot below.

Screenshot of CSI Simple C# Interpreter on Unity 3D

At the top is the interpreter’s output window, which is a read-only text area where additional output gets added at the bottom while the older output scrolls up. You can navigate to this area and copy some of the output text if you want.

Below the output window, on the left side, there’s a large toggle button that can be used to show or hide the rest of the GUI. You can either click on the button or use the default Alt+F2 shortcut to toggle the display state. Underneath the main toggle button you’ll find a couple of small buttons: the one on the left is a regular button that can be used to clear the output window when it gets too cluttered, the remaining buttons are all toggle buttons. Hover over the buttons with the mouse pointer to see tooltips that are shown in the bottom window to describe their purpose. The small toggle button at the top would show or hide the output window, and the one at the bottom does the same but for the tooltip bar. When running in the Editor, a third toggle button appears between these two and it controls whether the resulting objects that are returned by the interpreted C# code would be selected automatically in the Editor, for example to allow for additional editing or viewing through the Editor’s regular inspector windows.

The remaining text area is the input window where you enter the code to be interpreted. While the input window has focus, a number of keyboard keys are interpreted in special ways. For example, hitting the Enter key would send all of the input text to the C# interpreter for processing and typically result in code execution. To enter line-breaks in the input window, you can use Shift+Enter, but you can often also feed multi-line input to the interpreter in multiple steps using the Enter key by itself between steps; doing so, the prompt displayed on the large toggle-button would change from “>>>” to “...” that indicates the continuation state of the interpreter, as illustrated below.

>>>  for (int i = 1; i <= 3; i++) {
... print(UnityEngine.Random.value); }
0.4331492
0.1738945
0.5771966

Hitting the Enter key without any input text would reevaluate the previous input. You can use the up and down arrow-keys to navigate to previous input and then copy or edit it. To move the cursor between lines in a multi-line input, you need to hold down either Ctrl or Caps-Lock while pressing the arrow keys – it can take a while getting used to it, but even if you slip up and accidently navigate your history instead of the input lines, you can simply navigate the history in the opposite direction without losing earlier changes to the input text.

The input box also implements a rudimentary auto-completion or help system that gets triggered through the Alt+F1 shortcut; however, the context is typically the result of the previous step’s code (and not so much the current input text). What this means is that, to use this functionality, you’ll often end up executing an expression in incremental steps, each time navigating to the previous step’s input and appending more code to it while triggering the system. Even so, this feature can help a lot once you know how to use it. For example, as illustrated by the next session log, you can enter “DateTime.Now” (without the quotes) as input and press Enter to get the current time and make that result the current context for help; if you now press Alt+F1 you’ll get a long list of all the DateTime methods and properties available for this type of result (such as AddDays and Ticks). If you then enter “DateTime.Now.ToF” (most of which can be retrieved with the up arrow) and then press Alt+F1, the input-line would auto-complete to “DateTime.Now.ToFileTime” and show additional help: only two of the members will now show up as options (namely ToFileTimeUtc and ToFileTime), since the last portion of the input, “ToF”, is applied as a regular-expression filter to the context. The auto-completed text is the common portion of the filtered member names, and, because the auto-completed text also matches one of the member names exactly, the method prototype for ToFileTime is displayed as additional help in the output window.

>>>  DateTime.Now
(DateTime) 13/03/2010 2:28:19 PM
-------------------------------
.ctor AddDays AddHours AddMilliseconds AddMinutes AddMonths
AddSeconds AddTicks AddYears Add CompareTo Date DayOfWeek DayOfYear
Day Equals GetDateTimeFormats GetHashCode GetTypeCode GetType Hour
IsDaylightSavingTime Kind Millisecond Minute Month Second Subtract
Ticks TimeOfDay ToBinary ToFileTimeUtc ToFileTime ToLocalTime
ToLongDateString ToLongTimeString ToOADate ToShortDateString
ToShortTimeString ToString ToUniversalTime Year
-------------------------------
ToFileTimeUtc ToFileTime
-------------------------------
Int64 ToFileTime()

Furthermore, if you wanted help on the static members of the DateTime class, type “typeof(DateTime)” as input text followed by Enter to make the type itself the current help context, then press Alt+F1 to display static member names such as IsLeapYear and UtcNow, as shown below.

>>>  typeof(DateTime)
(Type) System.DateTime
-------------------------------
Compare DaysInMonth Equals FromBinary FromFileTimeUtc FromFileTime
FromOADate IsLeapYear MaxValue MinValue Now op_Addition
op_Equality op_GreaterThanOrEqual op_GreaterThan op_Inequality
op_LessThanOrEqual op_LessThan op_Subtraction ParseExact Parse
ReferenceEquals SpecifyKind Today TryParseExact TryParse UtcNow

Although the input text would primarily consist of C# code, the interpreter does have a preprocessor that extends the interpreter’s syntax. Refer to the original CSI documentation to get a feel for some of the possibilities using the preprocessor macros (for example, you can also display data-type metadata through a macro by typing “/M DateTime”). More importantly, the original documentation also explains how the dollar-sign session variables are implemented.

You use dollar-sign session variables to store and retrieve intermediate results in the interpreter, since the regular C# way of manipulating intermediate results as local variables does now work as you would expect. What it effectively boils down to is that, as an alternative, you have a global lookup table of variables that can be shared between consecutive interpreted steps. For example, instead of writing “int x=5” and “int y=x+3” as real C# variables, you would need to write “$x=5” and “$y=$x+3” where x and y are the names of the variables stored in the lookup table, as illustrated by the following CSI session:

>>>  $x=5
>>> $x
(int) 5
>>> $y=$x+3
>>> $y+1
(int) 9

The original CSI dollar-sign syntax has been extended for the Unity C# Interpreter plug-in. The mechanism will first try to interpret the names as those of variables you have defined, but, when retrieving variable values, it can fall back to some of the query mechanisms provided by Unity if the specified name is not a session variable. For example, if you type “$Sphere” and hit Enter, the interpreter would first look for a session variable named Sphere that may have been defined through an earlier assignment. But if that variable does not exist, the interpreter would look for a GameObject named Sphere and use that object if it exists. And if that also does not exist, it would try to find GameObject instances with the specified tag, or finally would try to interpret the specified name as that of an object type of which the instances need to be located. Therefore, writing something like “$Light” would typically give you quick access to all the light objects in your current game scene, since all those game objects would have a component of type UnityEngine.Light associated with them. To be able to include spaces or funny characters in the names, you wrap the name in curly brackets, for example, you can write “${Directional light}” and “${/Root Object/Child Object}”; also, “${Light}” and “$Light” means exactly the same thing.

Configuration Options through Inspector

CSI configuration options in Inspector window of Unity EditorWhen you have the GameObject with the CSI component selected in the Unity Editor, the Inspector window displays a number of configuration options (see image). Of course, these options are also available at runtime through programmatic access, although some of the values are only interpreted during the initialization phase of the interpreter. The configuration options are as follows:

  • Include File – This specifies the filename, either as a relative or absolute path, of the file that will be included during initialization, such as the default CSharpInterpreter_Include.txt file. Leave it blank if no file should be included.
  • Include Asset – Instead of specifying an include file though its filename, you can define a text asset in your project and drag-and-drop the asset (such as “CSharpInterpreter_Include”) from the Project window to this property to be included during initialization.
  • Queued Asset – This specifies a text asset that will be executed at runtime during the next update cycle as if it were entered as input text. Once executed, the property resets again. Its initial value can be used to specify another asset that must be executed immediately after initialization, but, more importantly, it can be used while the scene is playing in the Unity Editor to execute pre-written scripts for use at runtime. Therefore, you can build yourself a library of C# scripts that you want to execute through the interpreter and drag-and-drop those scripts at runtime to this property when needed.
  • Max History Size – This is the maximum number of items that will be kept in your input history for navigation.
  • Max Output Size – Specifies the maximum number of characters that will be displayed in the output window.
  • Show Interactive GUI, Show Output Text, Show Output As Editor Selection, Show Tooltip Text – These properties correspond with the toggle buttons on the runtime user-interface (discussed in the previous section).
  • Left Margin, Top Margin, Right Margin, Bottom Margin – These represent percentages of the screen width or height that surround the runtime user-interface (when fully expanded). Values set to NaN will be calculated automatically.
  • Toolbox Width – This represents the pixel width of the toolbox area to the left of the input window.
  • Splitter Fraction – Specifies an approximate percentage of the user-interface height (when fully expanded) that gets split between the output window and the controls below it.
  • Max Output Line Width, Max Output Line Count – With these options you can limit the maximum characters per line and the number of full lines that are written to the output window for results. (The output window will still automatically break the lines further if they are wider than the output window.)

Final Thoughts

While I’m on the topic of a project that extends the original CSI code, I would like to mention a related project by Samuel Christie called XNA Console, which sports a Python interpreter. Well, I also wrote a C# interpreter extension for the XNA Console, similar to this one for Unity and also based on CSI – it hasn’t been released publically, though, maybe someday if there is enough interest.

Also, if you look at the default CSharpInterpreter_Include.txt file, there is a line that would include the System.Linq namespace, but it is commented out. The reason for this is that the LINQ functionality that is included with the older version of Mono for Unity 2.6.1 is not fully functional (for one thing, Enumerable.Range doesn’t work quite right). But if you feel adventurous, add the namespace and see what is available; for example, doing so would make the following console interaction possible:

>>>  new int[]{7,13,19}.Select(x=>x*2).OrderByDescending(x=>x)
(Linq.OrderedSequence<int>)
{38,26,14}

Although the code above doesn’t seem to be possible in a regular Unity C# script, it does work in the C# interpreter. However, something more complex like “$Transform.Select(t=>t.position)” still doesn’t work because the Select extension method cannot be found. Hopefully the Unity team will upgrade their Mono version to a newer one before too long. With a newer version of Mono, one could perhaps even rewrite the plug-in to use Mono’s own csharp shell, which might improve speed and stability.

Oh, and it often helps to implement a pause feature for your game and use it while working in the interpreter, unless you don’t mind falling off a cliff while navigating the input history, or a monster destroying your $Player game-object before you hit Enter, or... you get the idea. Have fun!

Downloads:

Comments

very nice work

Thats some very nice work you did here.
I'm sure many will find ways to use it (and missuse it hehe)

As for the "hope the unity team upgrades mono": Its known that Unity 3 will go up to Mono 2.6+, so definitely yes :)

Sweet!

but one quick Q.

do I need to download both or is the unitypackage alone enough (no need for .zip?)

Just One

You only need to download one of the two files; whichever format you prefer. Of course, the ZIP file can be viewed more easily on computers that don't have Unity installed, so that you can quickly also look at the source code. But if you have Unity installed, and you like to import files that way into your projects, just use the Unity-package file.

Thanks

Thanks, a clear and to the point answer! <3

CSI on Unity 3

Hi Tiaan,

Seems interpreter currently could not work in Unity 3. What should we do to make it work?

Re: CSI on Unity 3

I guess we are out of luck on Unity 3. The Microsoft.CSharp namespace, which contains the CSharpCodeProvider class being used for dynamic compilation, seems to have been removed from some of Mono's included System.dll runtime files.

Re: CSI on Unity 3.*

...I really needed this...isn't there any way to overcome the Microsoft.CSharp namespace absence?

Cumps

It works in Unity 3

It works in Unity 3 - just make sure in your player settings your API comparability Level is .Net 2.0, and not just .Net 2.0 subset.

CSI version 0.8.24.3 does now work in Unity 3.0 and 3.1

Herman, thanks a lot for the tip! This setting seems to switch between Unity’s different System.dll versions (for example, with or without the Microsoft.CSharp namespace), and it was the first step to get CSI running on Unity 3.x. For the complete solution I also updated the CSharpInterpreter script to resolve Nab’s issue (and to include a few more minor enhancements).

In summary, the following procedure is used to get rid of “error CS0234: The type or namespace name `CSharp' does not exist in the namespace `Microsoft'. Are you missing an assembly reference?”: Access the “File | Build Setting” menu in the Unity 3.x Editor, ensure that “PC and Mac Standalone” is selected for the Platform, and then click the “Player Settings...” button. In the Inspector window, under Optimization, change the “API Compatibility Level” value from “.NET 2.0 Subset” to “.NET 2.0”. To force a code rebuild you might have to right-click on the CSharpInterpreter script in the Project window and select Reimport.

using CSI in Unity 3.1 using DLL

I got it to work in Unity 3.1 indie after compiling it to dll, but i still get an error in Unity's console

NullReferenceException: Object reference not set to an instance of an object
CSharpInterpreter.GetFullPathOfAssembly (System.Reflection.Assembly assembly)
CSharpInterpreter+InitializeCompilerForUnity3D.RunOnce ()
CSharpInterpreter.Reinitialize ()
CSharpInterpreter.Start ()

anyway, the way I did it is like this (using Visual Studio).

1- start a new Class Library project in VS.
2- add the source code files to the project.
3- add the UnityEngine.DLL to your project as a reference (found in "Unity\Editor\Data\Managed").
4- Build the solution (as release).
5- copy the dll file (from solution's bin folder) to your unity project (under plugins/CSI).
6- copy the include file along with the dll.
7- (not sure if this is required) Assets=>sync MonoDevelop project
8- you'll find the dll file as a collapsible with the public classes selectable, open it & you'll find the CSharpInterpreter.
the rest is as usual.

please test & let me know how it works out.

Using CSI as a DLL

@Nab: The updated CSI scripts should now work so that you would not have to build a separate DLL. However, I found it interesting to learn from your comment and the release notes of Unity 3.x that “Managed .NET DLLs can now be placed in the project folder and can contain script code [..., which] allows you to move any code into a DLL”. Although I haven’t tested it, this means that you should also be able to compile the updated CSI 0.8.24.3 scripts and now use it in DLL format as you describe.

Reuse of code

Hi!

Excellent work with the interpreter! However, I wanted to ask if it is possible to somehow reuse a previously compiled code by the interpreter so as to execute it multiple times. For example, could I somehow produce a DLL from a code chunk, interpret it with CSI, load it, and then call it from other scripts whenever I want without having to re-interpret it again?

TIA

clouDeng

Re: Reuse of code

Yes, it should be possible to use the code from a DLL, but you can only use .NET functionality provided by the Mono runtime of Unity. To do so, you could implement the "code chunks" as public static methods and compile it into a .NET assembly (using the standard Mono or .NET command-line compilers, or Visual Studio). Copy your DLL to a folder where CSI would find it (try one of the folders under Unity that contains System.Core.dll). Then customize your CSharpInterpreter_Include.txt file by adding a line that would reference your DLL (similar to the line "/r System.Core.dll"), and finally you could also add your namespace (with a line similar to "/n System.Text"). You should then be able to call your methods from CSI using code like "MyClass.MyStaticMethod(myParameters)".

Only partially

Well, it seems to (partially) work. I put my compiled dlls to the C:\Program Files\Unity\Editor\Data\MonoCompiler.framework and the editor finds it and uses it when i test it through it. However, when I build my scene to a standalone exe, the CSI can't find the dlls. Unity seems to correctly copy the dlls to the _Data folder, but CSI doesn't find them. I also tried to but the dlls in the "lib" subfolder in the _Data folder, but, to no luck.

Also, why can't CSI run in a web player?

(sorry if I'm asking too much, but CSI has completely filled up my needs for a specific script driven app :) )

Re: Only partially

Well, I am glad to hear it works in the Editor. For the Standalone application, you are somewhat on your own — your use-case is a bit beyond what I needed CSI for (namely, primarily as a debugging tool during development). And regarding your last question, if I recall correctly, CSI does not work in the Web Player because some required security rights are not allowed in the browser environment (which is probably a good thing, except for those who want to write browser viruses with Unity :).

Re: Reuse of code

hi all!

I'm kinda new to unity and I need to build an "extendable/programmable" runtime environment... Like having a game with a GUI text area in which, on runtime, it'd be possible to write code, full methods, that can be called any time.. at least on that execution, during that level...

Is it possible? Do you have any ideas of how to do this?

Use lambda expressions

@filixix: With CSI on Unity 3.x you can use lambda expressions to mimic “full methods”. For example, define a method using something like...
$f=new Func<int,int,int>((x,y)=>x*y+12)
To execute the method, write something like $f(3,10) — that would return 42 as an integer.

Error message

When i try to use it, the output area gives an error saying :
ERROR : File Name Has Not Been Set

Re: Error message

That sounds strange. Are you perhaps using Mac OS, since I only tested on Windows? Also, to get more error detail, find the ExecuteCode method in CSharpInterpreter.cs and temporarily change the error handling code to write out exception.ToString() instead of exception.Message (there is already an alternative line that you can uncomment).

Re: Error message

I'm using windows version, unity 3.1
roger that, will give it a try and let you know

Re: Error message

I'm having the same issue.

when i uncommented the line, the following error is displayed

ERROR: System.InvalidOperationException: File name has not been set
at System.Diagnostics.Process.Start_common (System.Diagnostics.ProcessStartInfo startInfo, System.Diagnostics.Process process) [0x00000] in <filename unknown>:0
at System.Diagnostics.Process.Start () [0x00000] in <filename unknown>:0
at (wrapper remoting-invoke-with-check) System.Diagnostics.Process:Start ()
at Mono.CSharp.CSharpCodeCompiler.CompileFromFileBatch (System.CodeDom.Compiler.CompilerParameters options, System.String[] fileNames) [0x00000] in <filename unknown>:0

I managed to narrow it down to line 1031 of interpreter.cs
return prov.CompileAssemblyFromSource(cp, finalSource);
if i surround it with try/catch, the caught exception will have the previous message !!

& thanks for the Great plugin :D

Re: Error message

It looks like there is something weird with your Unity setup, and CSI probably cannot find the compiler dependencies (gmcs.exe and/or mono.exe). You could run into an issue like this, for example, if you installed Unity into a non-default directory, and it is more likely to occur in the Standalone Player (such as on a machine where the Unity Editor is not installed).

I tested CSI again with a default installation of the latest version of Unity (3.1.0f4), and it works fine for me. What version of Windows are you running? What version of Unity 3D are you using? Do you get this error only in the Unity Editor, the Standalone Player, or both? In what directory do you have Unity installed? These answers could give some clues about what is different with your setup.

You could also try pointing CSI to the right directory by defining the CSI_COMPILER_PATH environment variable. Similar to the common PATH environment variable, you can specify multiple search directories separated by semicolons. (You also need to restart the Unity Editor for the value to propagate.)

Re: Error message (resolved)

very well done.

great analysis of the problem :D.

I can confirm that i had my unity installation in the D Drive.
i uninstalled & reinstalled at the default directory & it ran like a charm.

& I run a vista 64bit btw.
& the problem was with the editor, didn't really try with a build at the time.
but now i can confirm that both the editor & build work.

Thanks for the great plugin.
this should save a lot of time.

Support for customized Unity 3.x directory

The updated CSI version 0.8.24.4 now also supports installation of Unity 3.x in a non-default directory. I believe this feature worked with Unity 2.6 all along.

For the record, in case the issue surfaces ever again, an alternative workaround is to define the CSI_COMPILER_PATH environment variable with a value that specifies the directories for both gmcs.exe and mono.exe. For example, use D:\CustomPath\Data\Mono\lib\mono\2.0;D:\CustomPath\Data\Mono\bin if Unity 3.1 were installed to D:\CustomPath, but hopefully the new CSI version would make this step unnecessary.

Thank you

Thank you very much to Tiaan and Anonymous !
Yes, i installed it in a different directory called "Unity 3.1" as i had both 2.6 and 3.1 installed concurrently.
I was wondering why it worked for 2.6 but not 3.1.

"The classes in the module cannot be loaded"

This really is a cool component, and definitely something very useful. I'm running Unity 3.3, however, and am getting the following exception:

System.Reflection.ReflectionTypeLoadException: The classes in the module cannot be loaded.
at (wrapper managed-to-native) System.Reflection.Assembly:GetTypes (bool)
at System.Reflection.Assembly.GetTypes () [0x00000] in <filename unknown>:0
at Mono.CSharp.RootNamespace.ComputeNamespaces (System.Reflection.Assembly assembly, System.Type extensionType) [0x00000] in <filename unknown>:0
at Mono.CSharp.RootNamespace.ComputeNamespace (Mono.CSharp.CompilerContext ctx, System.Type extensionType) [0x00000] in <filename unknown>:0
at Mono.CSharp.GlobalRootNamespace.ComputeNamespaces (Mono.CSharp.CompilerContext ctx) [0x00000] in <filename unknown>:0
at Mono.CSharp.Driver.LoadReferences () [0x00000] in <filename unknown>:0
at Mono.CSharp.Driver.Compile () [0x00000] in <filename unknown>:0
at Mono.CSharp.Driver.Main (System.String[] args) [0x00000] in <filename unknown>:0

The actual error isn't quite including that first line -- I had to do some digging and Debug.Log the CompilerResults.Output array members to see that -- but the stack trace is what is being dumped based on the ShowErrors method. From the look of things it is properly finding the actual gmcs.exe file despite the fact that I have this installed in a nonstandard location, so I'm a bit baffled.

Note that this is a project that was originally in Unity 2.6, so it doesn't have namespaces defined for most things. However, it does have several plugin libraries that are .NET dlls. I don't know if that matters, or really what this particular error signifies (corrupt metadata? missing dll references?).

Thanks for any help!

Re: "The classes in the module cannot be loaded"

I do not recall seeing something like this before. CSI by itself works fine on Unity 3.3, and from version 0.8.24.4 of CSI the non-standard install folder should be okay. As you say, the error seems to indicate an issue with the type metadata of one of your dependencies; might be a missing DLL (or something else). Perhaps start from a simpler project where CSI does work and then add your code/libraries bit by bit until it breaks. There isn’t much in the trace to go by. And yes, the .NET libraries might be an issue, since Mono does not implement all .NET features. If you make use of .NET libraries, they may need to be compiled for .NET Framework 3.5 or less (not 4.0). Systematic elimination could be your best bet.

Re: "The classes in the module cannot be loaded"

Okay, thanks for that -- I'm not overly surprised, to be honest. I'll see what I can figure out as I have time to experiment with it!

Reference scripts

Hi there

Can I reference other components (scripts) using this. I just tried, but I get different errors. Is there a workaround, because it would be a pity if that is not possible. As an example, should the GetComponent method work?

Thanks

Re: Reference scripts

What errors do you get? GetComponent seems to work fine, for example, I can execute
Camera.main.GetComponent<GUILayer>()
or even something more complex such as
Camera.main.GetComponent<GUILayer>().HitTest(Input.mousePosition)
without any issue.

Compatibility problem

I have the same compatibility problem with an editor plugin DLL compiled for .NET 3.5 x86. Unfortunately I need this plugin. Is it possible to "blacklist" this plugin so CSharpInterpreter would ignore it?

Re: Compatibility problem

Andriy: To make this easier to diagnose, can you provide more information about your specific problem? For example, what editor plug-in causes the problem? Is the problematic plugin DLL available on the Asset Store so that I can debug the issue? Do you get the exact same Assembly.GetTypes error that Chris Park did, or does the stack trace look different? Does this happen at startup or when you execute some code in the CSI console? Can you elaborate on what you know about the .NET 3.5 x86 compilation of the DLL, or whatever else you may think is relevant? With only the information that is currently available, it is rather difficult to pinpoint the cause.

Re: Compatibility problem

OK, I'll try to provide as much info as possible:

It's our custom Editor plugin so unfortunately you cannot try it and I cannot send it to you, sorry. I'll try to get more info about what's so specific about it. We have another .NET DLL (not an editor plugin) in our project, compiled as .NET 3.5 and there's no issue with it.

Error message is almost the same, except I don't see the first line. Mine is:

CSI Simple C# Interpreter v.0.8.24.5 from Tiaan.com in CLR v.2.0.50727.1433 on Unity v.4.2.0f4
>>> test
Compiling string: 'test'
at (wrapper managed-to-native) System.Reflection.Assembly:GetTypes (bool)
at System.Reflection.Assembly.GetTypes () [0x00000] in <filename unknown>:0
at Mono.CSharp.RootNamespace.ComputeNamespaces (System.Reflection.Assembly assembly, System.Type extensionType) [0x00000] in <filename unknown>:0
at Mono.CSharp.RootNamespace.ComputeNamespace (Mono.CSharp.CompilerContext ctx, System.Type extensionType) [0x00000] in <filename unknown>:0
at Mono.CSharp.GlobalRootNamespace.ComputeNamespaces (Mono.CSharp.CompilerContext ctx) [0x00000] in <filename unknown>:0
at Mono.CSharp.Driver.LoadReferences () [0x00000] in <filename unknown>:0
at Mono.CSharp.Driver.Compile () [0x00000] in <filename unknown>:0
at Mono.CSharp.Driver.Main (System.String[] args) [0x00000] in <filename unknown>:0

The problem happens in console when I try to execute doesn't matter what.

Re: Compatibility problem

If the DLL was compiled with Visual Studio instead of with a Unity compiler, there is a chance that it is not 100% compatible with Unity's Mono runtime. You can try to compile the external DLL manually with the compiler included with Unity from the Windows command-line (for example, using "gmcs.bat"), and use the DLL it produces instead.

Even if that fixes the problem, it would be nice if we can find a way to work around these sorts of issues. The stack dump is not very helpful, since it doesn't seem to resemble any of the actual .NET APIs that get called directly from the CSI code at the time where this error apparently occurs. I suspect the error is produced during CompileAssemblyFromSource in CSI.Interpreter.CompileTemplate, but the problem may be caused by some of the values added earlier to the CompilerParameters. For example, it may be caused by adding a problematic assembly to ReferencedAssemblies, which is gathered from referenceList. If you're looking for a way to "blacklist" assemblies, you could include some logic in interpreter.cs around line 1061, "cp.ReferencedAssemblies.Add(r)", and see where that gets you. However, ideally one would prevent the problematic assembly from getting into referenceList in the first place. But all of this is just speculation at this point. You'll have to do some digging, probably somewhere in interpreter.cs on the machine where this happens.

Please keep me posted on your findings.

Application.get_datapath?

Hi! Thank you so much for this wonderful plug-in. However, I couldn't get it to work. I've looked through the comments and it seems like nobody has encountered this problem yet so I'd like to ask for your guideline.

Here's the error :

UnityEngine.Application:get_dataPath()
CSharpInterpreter:GetDefaultIncludeFilename() (at /Users/Hima/Dropbox/mydev/Unity/TestJurassic/Assets/Plugins/CSI/CSharpInterpreter.cs:106)
CSharpInterpreter:Reset() (at /Users/Hima/Dropbox/mydev/Unity/TestJurassic/Assets/Plugins/CSI/CSharpInterpreter.cs:150)
CSharpInterpreter:.ctor() (at /Users/Hima/Dropbox/mydev/Unity/TestJurassic/Assets/Plugins/CSI/CSharpInterpreter.cs:131)

I'm using Mac OS, which I suspect that it might be the source of the error. It happen during the getting default filename for the include file. Any help or guideline on how to fix this would be highly appreciated!

Fixed but found another problem

It seems like in Unity 3.3, they don't allow you to use Application.get_datapath in other thread except main thread. So I have to move the Reset method into Awake method.

While this make the game run file, it gave me an error when I tried to execute any code inside the interpreter console

ERROR : Error running gmcs : Cannot find the specified file

I really have no idea how to deal with this one. :(

Re: Application.get_datapath / Fixed but found another problem

You are likely the first one to try the CSI plugin on Mac OS, which I think may be the problem. The code in the InitializeCompilerForUnity3D.RunOnce() method often needs to be updated for different versions of Unity, and I suspect one would need to add Mac-specific logic to it. Ideally one would figure out what needs to be added so that the method can automatically determine the correct values for the mcsPath and monoPath variables. However, you could also first try setting the CSI_COMPILER_PATH environment variable; refer to my comment of 2010-12-28, titled “Support for customized Unity 3.x directory”, for details.

Re: Application.get_datapath?

Indeed, CSI version 0.8.24.5 also fixed the Application.get_datapath error by moving the Reset method into the Awake method.

Problems with Unity 3.5.3f

When adding a GameObeject with the CSharpInterpreter script I get this error at run-time:

get_dataPath can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

If I just skip the error then it seems to work perfectly afterwards.

Any ideas on how to fix it?

BTW, thanks for a great tool :o)

Cheers,
Jesper

Re: Problems with Unity 3.5.3f

Hi Jesper,

I wonder in what version of Unity this error first cropped up, since I have not recently used the C# Interpreter in Unity. I now tested CSI on Unity 3.5.0f5 and got the same message; perhaps this changed with Unity 3.5, or may it has been 3.0?

I suppose it is time to release a new version of CSI for Unity 3D. However, until such time, it looks like one just needs to turn the CSharpInterpreter constructor into an Awake function. To do so, replace the constructor definition on line 127 in the file CSharpInterpreter.cs with the following code:

//public CSharpInterpreter()
private void Awake()

This should still be tested some more, but from a quick assessment it appears to fix the problem. Strangely enough (like you said), even if one just ignores the error, CSI seems to still work, although it might be skipping the initialization of some data fields. I am also curious to know whether the Awake function have always been around.

Re: Problems with Unity 3.5.3f

Thanks for the quick reply :o)

I think Awake has at least been around since Unity 2.6.

CSI version 0.8.24.5 now available

A new version of the C# Interpreter for Unity 3D has been released, which fixes the issue reported by Jesper Rasmussen. As always, the download link is available at the end of the original post.

The only code change is that the CSharpInterpreter constructor has been turned into an Awake method. The new CSI version 0.8.24.5 has been tested and appears to work fine on Windows with both Unity 2.6.1f3 and Unity 3.5.0f5, which means that Awake has indeed been around since Unity 2.6 or earlier.

Furthermore, based on feedback received earlier this week from Hima, it seems likely that this new version will even work with Unity 3.5 on Mac OS 10.8 — it would be great if someone with a Mac can confirm this with the official release.

Source code at public VCS?

Hello, Tiaan!
I'm very happy to find such a useful plugin, this is exactly what I was looking for!
Don't you want to put its source code on github, for example? It would be very cool to track new releases and be able to get the plugin version for every new (and old too) version of Unity via github, not by constantly checking this blog for updates.

Re: Source code at public VCS?

@Skyblade: I’ve been contemplating putting the code on GitHub for a while and think it is a good idea indeed. However, not knowing if anyone would actually care about it, I just haven’t taken the time to do so. I have the various older and newer revisions in a personal Subversion repository, such that adding all the versions in a new GitHub repository is certainly possible. It looks like you already discovered my GitHub page (and I see you also have some Unity 3D projects on GitHub), so I assume you’ll get notified as soon as I make time for this. Thanks for the encouragement! :)

Source code at GitHub

The CSI Console for Unity is now also available as a public Git repository that is hosted at GitHub (and referenced in the Downloads section). Furthermore, older versions of the software are identified through Tags in the repository. Although the current version 0.8.24.5 should still work on older versions of Unity, the Tags allow one to download any previously released version as a ZIP archive.

Thanks

Thank you very much!

Problems on Mac OS with Unity 4

Hi Tiaan,

Great plugin, thanks for offering it for free! I have troubles to get it running on my Mac (OS X 10.7.4) with Unity Indy 4.0 and iOS Basic. As first step I try to build a Mac standalone app (later I want to try iOS), but I can't get it running on Mac.
I am getting "ERROR: Error running gmcs: Cannot find the specified file".
I tried to set the Path variable to the Unity Mono directory, but it does not seem to work. Any idea?

Some additional questions:

  1. Should this work on Unity Indy when I download the Unity Package (or is Unity Pro necessary for this plugin)?
  2. Is it possible to use the interpreter on iOS? Especially in Unity iOS basic version? (This would be my final goal).
  3. Maybe you can clarify in more detail where to change the path variable or the "InitializeCompilerForUnity3D" function for Unity 4 and Mac.
  4. Can I directly use Unity commands in the interpreted code, or add new commands for the interpreter?
    1. Thanks for your support in advance - really a great plugin! You should put it on Unity Asset Store.

Re: Problems on Mac OS with Unity 4

@John: Well, I know that the plugin does still work with Unity 4.0 on PC, so it should be possible to get it to work on Mac OS X (at least for OS X 10.8, but hopefully also 10.7). Since I am not sure exactly how you already “tried to set the Path variable”, I’ll again recommend defining the CSI_COMPILER_PATH environment variable to specify the directory of gmcs.exe; refer to my comment of 2010-12-28. You may also need to restart Unity fresh to test any changes, just in case.

Regarding your other questions:

  1. You do not need Unity Pro for this plugin. The free version also works fine.
  2. I very much doubt that the interpreter will work on an iPad or iPhone, since I believe the gmcs.exe compiler does not get exported when you build to iOS. (Similarly, the plugin does not work in the Unity Web Player.)
  3. You need to define the CSI_COMPILER_PATH variable as you would do for any other environment variable. Because I do not have a Mac, I am not sure exactly how you need to do that, but perhaps refer to the Environment Variables document over at the Mac Developer Library as a starting point; the section on “User Session Environment Variables” and the “~/.MacOSX/environment.plist” file (that is a Property List) appear to be relevant. Since this file might only be read at login time, you may also need to restart your Mac or re-login for a change to take effect. As a last resort, you could hard-code the path by adding code such as “searchPaths.Add("/Path/To/gmcs/Folder");” to the CSharpInterpreter.cs file, somewhere around line 1683, but I do not really recommend this approach. Furthermore, the summary on Natascha’s blog may be helpful, and her “export” method to set environment variables is how I often do it on various other Linux-variant operating-systems where the export line can also be added to your shell’s log-in script. So, perhaps adding something like “export CSI_COMPILER_PATH=/Path/To/gmcs/Folder” to the “~/.bash_profile” file might do the trick.
  4. Yes, you can use “Unity commands” in CSI, as well as add new commands. You might want to add such code as static methods to custom C# classes and then define short-hand macros to those methods in the CSharpInterpreter_Include.txt file.

Re: Problems on Mac OS with Unity 4

Thanks Tiaan for your fast feedback. I changed the path as described, but also did not get it working. I found in some forums that I might need to directly install the mono runtime, this helped on Mac in similar situations (not necessary on windows). But as you assume the interpreter will not run on mobile, it might be the wrong approach for me and I am evaluating alternatives.
When I try again and get it running, I will post it. Thanks a lot for your effort and your detailed answers!

Another console window?

Hi Tiaan

I would like to thank you for making the CSI compatible with Unity, it's taken a little bit of trail and error but so far it's working wonders :)

I have a question for you and hopefully you will be able to help. I am looking to make the 'Console' button hidden and add a custom console window using a GUILayout.TextField which I would like to input code to run in the CSI console. I have worked out how to hide your console (CSI) button but I'm struggling to find how to pass the code from one (mine) console window to the other (yours). Any idea how to pass code from to another?

Any help would be greatly appreciated and feel free if necessary to email me :)

Again, great job with the plugin.
Thanks
Bob