Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
This post has been wikified, any user with enough reputation can edit it.
avatar image
12
Question by Alizarin · Dec 15, 2012 at 09:32 PM · c#build-errorcompilerin-game

In-Game scripting works in Editor (Try it!) - But Not Build

The Idea

  • want players to be able to interact with the game through C# scripting. Everything works in the editor (go ahead an try it), but not when I "build" the game. This is the error (exception message) I see:**


    The Error Message

    n exception was thrown by the type initializer for Mono.CSharp.CSharpCodeCompiler System at Microsoft.CSharp.CSharpCodeProvider.CreateCompiler () [0x00000] in :0 at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource (System.CodeDom.Compiler.CompilerParameters options, System.String[] fileNames) [0x00000] in :0 at ScriptEditor.Compile () [0x00000] in :0


    The Code

    o Run this script in your game:

    1. Create a C# script file "ScriptEditor.cs "

    2. Paste this code.

    3. Attach to any game object

    4. In Build Settings->Player Settings, change ".NET 2.0 Subset" to ".NET 2.0"


      using UnityEngine; using System; using System.Text; using System.Collections; using System.Reflection; using System.CodeDom; using System.CodeDom.Compiler; using Microsoft.CSharp;

      ///

      /// ScriptEditor.cs (Attach this to any game object) /// Useage Notes: /// For this to run, you MUST: /// 1. Go to File-->Build Settings... /// 2. Click "Player Settings..." /// 3. "Other Settings" /// 4. "Optimization"-->"API Compatability Level" /// 5. Change from ".NET 2.0 Subset" --> ".NET 2.0" ///

      public class ScriptEditor : MonoBehaviour {

        void Update()
           {
               // Dont run our script until it has been COMPILED successfully.
               if(myScript_Type == null || myScript_Instance == null) return;      
               
               // Run the script's Update() function
               myScript_Type.InvokeMember("Update",BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, myScript_Instance, null);
               
               // Read the scripts "readme" variable.
               PropertyInfo readmePropertyInfo = myScript_Type.GetProperty("readme");        // Source: http://www.csharp-examples.net/reflection-examples/
               readme = (string)readmePropertyInfo.GetValue(myScript_Instance, null);
           }
           
           private string scriptText = 
       @"using System;
       using System.Collections.Generic;
       
       public class MyScript
       {
           public string readme {get;set;}  // Our unity program will read this variable.
           private int counter = 0;    
       
           public MyScript() // Constructor
           {
               readme = """";
           }
       
           public void Update()  // Our unity game will run this function.
           {
               counter++;        
               readme = ""Hi! Script is Running! "" + counter + ""\n"";
           }
       
       
       }
       ";
           
           private string readme = "";            // Displayed on the GUI
           private string compilerErrorMessages = "";        // Displayed on the GUI
       
           void OnGUI() 
           {
               // ********** Display the script in Unity
               scriptText = GUI.TextArea(new Rect(10, 10, 700, 500), scriptText);
               
               // ********** Compile your script
               if (GUI.Button(new Rect(720, 10, 300, 30), "Compile and Run"))
               {                            
                   Compile ();      // Compile the script. Write errors to "compilerErrorMessages", if any.
               }
               
               // ********** Display any compiler errors
               GUI.TextArea(new Rect(10, 510, 700, 50), compilerErrorMessages);  // The console for displaying errors.
               
               // ********** Display the Script's Output.
               GUI.TextArea (new Rect(720, 50, 300, 30), readme);     
           }
           
           private Assembly generatedAssembly;                    // Compiled code is called an "Assembly"
           private Type myScript_Type = null;                    // These two variables are used run the compiled code.
           private object myScript_Instance = null;            // These two variables are used run the compiled code.
           
           private void Compile()
           {
               try
               {
                   compilerErrorMessages = "";  // clear any previous messages
                   
                   // ********** Create an instance of the C# compiler   
                   CSharpCodeProvider codeProvider = new CSharpCodeProvider();
                   
                   // ********** add compiler parameters
                   CompilerParameters compilerParams = new CompilerParameters();
                   compilerParams.CompilerOptions = "/target:library /optimize /warn:0"; 
                   compilerParams.GenerateExecutable = false;
                   compilerParams.GenerateInMemory = true;
                   compilerParams.IncludeDebugInformation = false;
                   compilerParams.ReferencedAssemblies.Add("System.dll");
                   compilerParams.ReferencedAssemblies.Add("System.Core.dll");
                   
                   // ********** actually compile the code  ??????? THIS LINE WORKS IN UNITY EDITOR --- BUT NOT IN BUILD ??????????
                   CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams,scriptText);
                   
                   // ********** Do we have any compiler errors
                   if (results.Errors.Count > 0)
                   {
                       foreach (CompilerError error in results.Errors)
                           compilerErrorMessages = compilerErrorMessages + error.ErrorText + '\n';
                   }
                   else
                   {
                       // ********** get a hold of the actual assembly that was generated
                       generatedAssembly = results.CompiledAssembly;
                           
                       if(generatedAssembly != null)
                       {
                           // get type of class Calculator from just loaded assembly
                           myScript_Type = generatedAssembly.GetType("MyScript");
                           
                           // create instance of class MyScript
                           myScript_Instance = Activator.CreateInstance(myScript_Type);
                           
                           // Say success!
                           compilerErrorMessages = "Success!";
                       }
                   }
               }
               catch(Exception o)
               {
                   compilerErrorMessages = ""+o.Message +"\n"+ o.Source +"\n"+ o.StackTrace +"\n";
               }
               
           }
           
       }
       
      
Comment
Add comment · Show 9
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Aubrey-Falconer · Dec 16, 2012 at 04:57 PM 0
Share

Very cool implications here. Have you experimented with sandboxing to render this a safe game modding system?

avatar image benjaminjhobbs · Jun 05, 2013 at 02:23 PM 0
Share

I am attempting something quite similar to what you list here and I am see the same issue. Have you had any success in the last 6 months with this?

I noticed in the $$anonymous$$ono code you've linked to below that the static constructor is starting with a private property of Environment GacPath and navigating relative to that for mono.exe and gmcs.exe. So I compared the GacPath between my Editor version and Build version and made local copies of those folders. This is an ugly workaround, and strangely only worked when I choose to Build and Run from the Editor, it still didn't run my new code when started it by double clicking the executable in the folder.

avatar image Bunny83 · Jun 05, 2013 at 03:47 PM 0
Share

In any case you have to ship the Cä compiler with your Unity-game since you can't rely on the fact that the user has a $$anonymous$$ono version installed (or .NET). This page might be helpful but you probably have to dig quite a bit into the $$anonymous$$ono code to get it working ;)

As alternative either search if someone implemented a C# compiler library that can be easily included into a Unity or C# application or use a simpler scripting language like LUA. Just if you haven't seen there's a lua implementation, modified for Unity3d, called UniLUA

avatar image Alizarin · Jun 17, 2013 at 03:47 AM 0
Share

That is awesome! I can not wait to try the lua implementation. This would be just fine for my application. Thank you Bunny83

avatar image Xtro · Jun 23, 2014 at 08:28 PM 0
Share

LUA is something different and I love C#. I would be happy if it was possible to add an addon system to my game depending on C# language.

Have you found a solution for including the mono compiler into the game build ?

Show more comments

3 Replies

· Add your reply
  • Sort: 
avatar image
4

Answer by Bunny83 · Dec 16, 2012 at 05:18 PM

The answer is quite simple. If you use ILSpy or any other .NET reflector you will see that the Mono.CSharp.CSharpCodeCompiler has a static constructor which searches for the Mono SDK.

I'm not sure why so many implementetions catch exceptions to rethrow them in a cryptical manner. When you remove your try-catch block and run your code in a standalone build, you will see something like that in your output_log.txt:

 FileNotFoundException: Windows mono path not found: D:\Unity35\Projects\mono\mono\mini\mono.exe
   at Mono.CSharp.CSharpCodeCompiler..cctor () [0x00000] in <filename unknown>:0 
 Rethrow as TypeInitializationException: An exception was thrown by the type initializer for Mono.CSharp.CSharpCodeCompiler
   at Microsoft.CSharp.CSharpCodeProvider.CreateCompiler () [0x00000] in <filename unknown>:0 
 
   at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource (System.CodeDom.Compiler.CompilerParameters options, System.String[] fileNames) [0x00000] in <filename unknown>:0 
 
   at ScriptEditor.Compile () [0x00000] in <filename unknown>:0 
 
   at ScriptEditor.OnGUI () [0x00000] in <filename unknown>:0

This:

 D:\Unity35\Projects\

is actually not my project path. It's actually:

 D:\Unity35\Projects\TestProject\build

TestProject is the project folder with the asset folder. The build folder contains the whole build.

It's a bit strange how Unity's mono version searches for the path. this is the static constructor:

 static CSharpCodeCompiler()
 {
     if (Path.DirectorySeparatorChar == '\\')
     {
         PropertyInfo property = typeof(Environment).GetProperty("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
         MethodInfo getMethod = property.GetGetMethod(true);
         string directoryName = Path.GetDirectoryName((string)getMethod.Invoke(null, null));
         CSharpCodeCompiler.windowsMonoPath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(directoryName)), "bin\\mono.bat");
         if (!File.Exists(CSharpCodeCompiler.windowsMonoPath))
         {
             CSharpCodeCompiler.windowsMonoPath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(directoryName)), "bin\\mono.exe");
         }
         if (!File.Exists(CSharpCodeCompiler.windowsMonoPath))
         {
             CSharpCodeCompiler.windowsMonoPath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(directoryName))), "mono\\mono\\mini\\mono.exe");
         }
         if (!File.Exists(CSharpCodeCompiler.windowsMonoPath))
         {
             throw new FileNotFoundException("Windows mono path not found: " + CSharpCodeCompiler.windowsMonoPath);
         }
         CSharpCodeCompiler.windowsMcsPath = Path.Combine(directoryName, "2.0\\gmcs.exe");
         if (!File.Exists(CSharpCodeCompiler.windowsMcsPath))
         {
             CSharpCodeCompiler.windowsMcsPath = Path.Combine(Path.GetDirectoryName(directoryName), "lib\\net_2_0\\gmcs.exe");
         }
         if (!File.Exists(CSharpCodeCompiler.windowsMcsPath))
         {
             throw new FileNotFoundException("Windows mcs path not found: " + CSharpCodeCompiler.windowsMcsPath);
         }
     }
 }

I guess since Unity brings it's own Mono compiler it's not installed in your system. When you run your game inside the editor it uses different assemblies which are shared with the editor.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Alizarin · Dec 17, 2012 at 12:34 AM 0
Share

Bunny83 Thank you so much for you reply! Very encouraging!

  • I tried installing two different versions of $$anonymous$$ono.

  • I tried placing the files I thought it was looking for (indicated in output_log.txt) in "...mono\\mono\\$$anonymous$$i\\" etc.

No luck so far.

How would you recommend I include the "$$anonymous$$ono compiler" in my game build? Is there a "mono.dll" I can place in my assets folder?

What are your thought?

avatar image
3

Answer by aeroson · Apr 05, 2015 at 12:18 PM

Solved it.

Installed Mono in default path: C:\Program Files (x86)\Mono (used for editor), then copied the Mono folder next to my standalone .exe as well (used for standalone).

alt text

Now i know where Mono is located so i just needed to edit the paths of mono.exe and mcs.exe. So i modified the CSharpCodeCompiler (link provided by Alizarin). And used this modified class to create the assembly.

Here source to the modification and it works both in editor and standalone. Thank you all.

Comment
Add comment · Show 7 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image aeroson · Apr 09, 2015 at 07:48 PM 0
Share

Another solution is to take the $$anonymous$$CS compiler source code which is written in C# and compile it for .NET 3.5

You can try referencing the mcs.exe it self as it is now, but it is compiled with .NET 4.0 so it doesn't work with Unity.

avatar image aeroson · Oct 18, 2015 at 12:06 AM 1
Share

Here is the definitive answer to this cause: https://github.com/aeroson/mcs-ICodeCompiler

avatar image Xtro aeroson · Oct 18, 2015 at 12:11 AM 0
Share

Perfect work! Also very good to include info about what they did for Cities: Skylines.

avatar image Rallymen007 aeroson · Feb 22, 2016 at 12:42 PM 0
Share

Great work, but the compile from DO$$anonymous$$ method doesn't seem to work. I get a class cast exception on line 120, right when you try to cast this into an ICodeGenerator. I'm looking into this issue and am trying to find a workaround, but if you have any idea of what's wrong any help is appreciated.

Never$$anonymous$$d, found a solution. I'll post it below.

avatar image gregtom7 · Nov 12, 2017 at 01:35 PM 0
Share

How can I use only the default solutions (provided only by $$anonymous$$icrosoft and/or $$anonymous$$ono at all) to be able to compile c# codes at runtime? I don't want to use ICodeCompiler you made at all, to understand better the whole thing.

So I get the "Windows mono path not found: F:/mono/mono/$$anonymous$$i/mono.exe" error message, if I want to compile in build. You said you have copied the installed $$anonymous$$ono folder near your game.exe. But where can you find CSharpCodeCompiler? And how can you create an assembly with that?

avatar image aeroson gregtom7 · Nov 12, 2017 at 01:57 PM 0
Share

You need to understand why it doesn't work now by looking into the source code of what is your Unity using (CSharpCodeCompiler class), then fix it. CSharpCodeCompiler is just starting new process ($$anonymous$$ono\lib\mono\4.5\mcs.exe) to compile your code, that means you can create new modified CSharpCodeCompiler to find correct mcs.exe

avatar image mohsalim · Mar 19, 2018 at 11:58 PM 0
Share

It seems they took down the source $$anonymous$$ono code. Is there anywhere I can view it?

avatar image
1

Answer by Rallymen007 · Feb 23, 2016 at 02:31 PM

Building on aeroson's answer I managed to get Unity to build C# in standalone in a much simpler way, though not as clean and space-efficient as his improved answer that comes with a patched version of mcs. This version is probably more suitable to bigger projects, for which including a 400 MB Mono runtime isn't an issue, if you want to have DLLs generated (to reuse them afterwards for example), or do not want to cope with all the little quirks of using Reflection.Emit instead of a good old compiled assembly. This version also fully works with CodeDOM objects.

I reused aeroson's CSharpCodeCompiler, which I copied into a CustomCSharpCodeCompiler.cs file inside my project. Then I added a slightly modified CSharpCodeGenerator using the Mono source code, which I copied into a CustomCSharpCodeGenerator.cs file inside my project. The main modification to this file was moving it to the Modified.Mono.CSharp package where the CSharpCodeCompiler is, which enabled the generator to use the custom compiler.

And here's the bit of script that uses those classes, it's a simple function that takes a CodeDOM object, a filename (used to determine the name of the assembly to compile), compiles it into a DLL file and loads this assembly in memory so you can use it straight away.

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Lee7 · Jan 06, 2018 at 06:27 AM 0
Share

This worked, thanks much.

I think this is actually a cleaner solution. Than the recompiled $$anonymous$$CS (which doesnt work with Visual Studio Integration tools because of conflicts)

avatar image aeroson · Jan 06, 2018 at 01:29 PM 0
Share

@Lee7 there are workarounds for that in issues

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

23 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Multiple Cars not working 1 Answer

The name 'Joystick' does not denote a valid type ('not found') 2 Answers

Project scripts stop working after building it in Android 1 Answer

Unity Internal Compiler Error, not there in Editor 0 Answers

Distribute terrain in zones 3 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges