RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Multiple Folder Compilation

Ki Frost

Sorceror
Im making a compiler that sits on-top of the distro compiler, it compiles a defined 'list' of folders.

I have it semi-working, but I have an issue im not entirely sure how to resolve.

Script:
Code:
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Security.Cryptography;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using System.Diagnostics;

namespace Server
{
    public static class MultiCompiler
    {
        public static string[] ScriptPath = new string[]
        {
            "Scripts\\Distros\\",
            "Scripts\\Modified\\",
            "Scripts\\Customs\\",
        };

        private static List<string> m_AdditionalReferences = new List<string>();

        private delegate CompilerResults Compiler(bool debug);

        public static bool Compile()
        {
            return Compile(false);
        }

        public static bool Compile(bool debug)
        {
            return Compile(debug, true);
        }

        public static bool Compile(bool debug, bool cache)
        {
            if (ScriptPath.Length <= 0)
            {
                Console.WriteLine("Scripts: Compiling C# scripts... no directories defined.");
                return false;
            }

            for (int i = 0; i < ScriptPath.Length; i++)
            {
                ScriptCompiler.EnsureDirectory(ScriptPath[i]);
                ScriptCompiler.EnsureDirectory((ScriptPath[i] + "Output\\"));
            }

            if (m_AdditionalReferences.Count > 0)
                m_AdditionalReferences.Clear();

            List<Assembly> assemblies = new List<Assembly>();

            for (int i = 0; i < ScriptPath.Length; i++)
            {
                string dir = ScriptPath[i].Remove(0, (ScriptPath[i].IndexOf("\\") + 1));
                dir = dir.Remove(dir.Length - 1);

                Console.Write("Scripts: Compiling C# scripts... [{0}] ", dir);

                List<string> files = new List<string>();
                ScriptCompiler.GetScripts(files, ScriptPath[i], "*.cs");

                if (files.Count == 0)
                {
                    if (i == 0)
                    {
                        Console.WriteLine("no files found.");
                        return false;
                    }
                    else
                    {
                        Console.WriteLine("-skipped (empty)");
                        continue;
                    }
                }

                Assembly assembly;

                if (CompileCSScripts(ScriptPath[i], files, debug, cache, out assembly))
                {
                    if (assembly != null)
                    {
                        assemblies.Add(assembly);
                    }
                }
                else
                {
                    return false;
                }

                files = new List<string>();
            }

            if (assemblies.Count == 0)
            {
                return false;
            }

            ScriptCompiler.Assemblies = assemblies.ToArray();

            Console.Write("Scripts: Verifying...");

            Stopwatch watch = Stopwatch.StartNew();

            Core.VerifySerialization();

            watch.Stop();

            Console.WriteLine("done ({0} items, {1} mobiles) ({2:F2} seconds)", Core.ScriptItems, Core.ScriptMobiles, watch.Elapsed.TotalSeconds);

            return true;
        }

        public static bool CompileCSScripts(string dir, List<string> files, bool debug, bool cache, out Assembly assembly)
        {
            if (File.Exists((dir + "Output\\Scripts.CS.dll")))
            {
                if (cache && File.Exists((dir + "Output\\Scripts.CS.hash")))
                {
                    try
                    {
                        byte[] hashCode = GetHashCode((dir + "Output\\Scripts.CS.dll"), files.ToArray(), debug);

                        using (FileStream fs = new FileStream((dir + "Output\\Scripts.CS.hash"), FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            using (BinaryReader bin = new BinaryReader(fs))
                            {
                                byte[] bytes = bin.ReadBytes(hashCode.Length);

                                if (bytes.Length == hashCode.Length)
                                {
                                    bool valid = true;

                                    for (int i = 0; i < bytes.Length; ++i)
                                    {
                                        if (bytes[i] != hashCode[i])
                                        {
                                            valid = false;
                                            break;
                                        }
                                    }

                                    if (valid)
                                    {
                                        assembly = Assembly.LoadFrom((dir + "Output\\Scripts.CS.dll"));

                                        if (!m_AdditionalReferences.Contains(assembly.Location))
                                        {
                                            m_AdditionalReferences.Add(assembly.Location);
                                        }

                                        Console.WriteLine("done (cached)");

                                        return true;
                                    }
                                }
                            }
                        }
                    }
                    catch
                    {
                    }
                }
            }

            DeleteFiles(dir, "Scripts.CS*.dll");

            using (CSharpCodeProvider provider = new CSharpCodeProvider())
            {
                string path = GetUnusedPath(dir, "Scripts.CS");

                List<string> getRefAssemblies = new List<string>(ScriptCompiler.GetReferenceAssemblies());
                getRefAssemblies.AddRange(m_AdditionalReferences); //MultiCompiler references...

                string[] refAssemblies = getRefAssemblies.ToArray();

                CompilerParameters parms = new CompilerParameters(refAssemblies, path, debug);

                string defines = ScriptCompiler.GetDefines();

                if (defines != null)
                    parms.CompilerOptions = defines;

                if (Core.HaltOnWarning)
                    parms.WarningLevel = 4;

#if !MONO
                CompilerResults results = provider.CompileAssemblyFromFile(parms, files.ToArray());
#else
                parms.CompilerOptions = String.Format( "{0} /nowarn:169,219,414 /recurse:Scripts/*.cs", parms.CompilerOptions );
                CompilerResults results = provider.CompileAssemblyFromFile( parms, "" );
#endif
                m_AdditionalReferences.Add(path);

                ScriptCompiler.Display(results);

#if !MONO
                if (results.Errors.Count > 0)
                {
                    assembly = null;
                    return false;
                }
#else
                if( results.Errors.Count > 0 ) {
                    foreach( CompilerError err in results.Errors ) {
                        if ( !err.IsWarning ) {
                            assembly = null;
                            return false;
                        }
                    }
                }
#endif
 
                if (cache && Path.GetFileName(path) == "Scripts.CS.dll")
                {
                    try
                    {
                        byte[] hashCode = GetHashCode(path, files.ToArray(), debug);

                        using (FileStream fs = new FileStream((dir + "Output\\Scripts.CS.hash"), FileMode.Create, FileAccess.Write, FileShare.None))
                        {
                            using (BinaryWriter bin = new BinaryWriter(fs))
                            {
                                bin.Write(hashCode, 0, hashCode.Length);
                            }
                        }
                    }
                    catch
                    {
                    }
                }

                assembly = results.CompiledAssembly;
                return true;
            }
        }

        public static string GetUnusedPath(string dir, string name)
        {
            string path = Path.Combine(Core.BaseDirectory, String.Format((dir + "Output\\{0}.dll"), name));

            for (int i = 2; File.Exists(path) && i <= 1000; ++i)
                path = Path.Combine(Core.BaseDirectory, String.Format((dir + "Output\\{0}.{1}.dll"), name, i));

            return path;
        }

        public static void DeleteFiles(string path, string mask)
        {
            try
            {
                string[] files = Directory.GetFiles(Path.Combine(Core.BaseDirectory, (path + "Output")), mask);

                foreach (string file in files)
                {
                    try { File.Delete(file); }
                    catch { }
                }
            }
            catch
            {
            }
        }

        private static byte[] GetHashCode(string compiledFile, string[] scriptFiles, bool debug)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bin = new BinaryWriter(ms))
                {
                    FileInfo fileInfo = new FileInfo(compiledFile);

                    bin.Write(fileInfo.LastWriteTimeUtc.Ticks);

                    foreach (string scriptFile in scriptFiles)
                    {
                        fileInfo = new FileInfo(scriptFile);

                        bin.Write(fileInfo.LastWriteTimeUtc.Ticks);
                    }

                    bin.Write(debug);
                    bin.Write(Core.Version.ToString());

                    ms.Position = 0;

                    using (SHA1 sha1 = SHA1.Create())
                    {
                        return sha1.ComputeHash(ms);
                    }
                }
            }
        }
    }
}

Result:
RunUO - [www.runuo.com] Version 2.1, Build 4151.28729
Core: Running on .NET Framework Version 4.0.30319
Core: Running with arguments: -debug
Core: Optimizing for 2 64-bit processors
Scripts: Compiling C# scripts... [Distros] done (0 errors, 0 warnings)
Scripts: Compiling C# scripts... [Modified] -skipped (empty)
Scripts: Compiling C# scripts... [Customs] done (0 errors, 0 warnings)
Scripts: Verifying...done (4582 items, 1060 mobiles) (1.21 seconds)
Error:
System.Reflection.TargetInvocationException: Exception has been thrown by the ta
rget of an invocation. ---> System.Exception: A poison with that level already e
xists.
at Server.Poison.Register(Poison reg)
at Server.PoisonImpl.Configure() in c:\Dropbox\Nagashizar 3.0\Server\Scripts\
Distros\Misc\Poison.cs:line 19
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Ob
ject target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAt
tributes, RuntimeType typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Obj
ect target, Object[] arguments, Signature sig, MethodAttributes methodAttributes
, RuntimeType typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisib
ilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture)
at Server.ScriptCompiler.Invoke(String method)
at Server.Core.Main(String[] args)
This exception is fatal, press return to exit


I changed the reference collection to include the MultiCompiler additions, and it gives that, but without the MultiCompiler additions, it doesnt include any of the lower-level scripts\folders.

Any help is appreciated. Ty
 

Lichtblitz

Sorceror
Configure() was most likely called twice.
Show the script that calls the Configure methods (originally ScriptCompiler.cs). Maybe you're adding the assemblies multiple times to the Assemblies list and thus Configure of the same class gets called multiple times?
 

Pure Insanity

Sorceror
Someone has made something like this before in the past. Believe Lokai and Jeff did their own versions. Although I believe Jeff's had issues. Lokai's was call the work bench mod I believe. It would be a good starting point.
 

Ki Frost

Sorceror
I know both Jeff and Lokai did something similar, I looked at Lokais for pointers and couldnt figure out the issue. The reason I dont want to use theirs is because I dont want to mod any files I dont have to, thus I am making my own while semi-replicating theirs.

I havnt changed anything in ScriptCompiler.cs, This one "sits on top" of it. It uses calls to script compiler, but everything else was replicated. The only thing I changed was the call from
while (ScriptCompiler.Compile(....
to
while (MultiCompiler.Compile(....

in Main.cs; all the rest of it is the same.

I am fairly certain it has to do with building the reference assemblies. If i use the regular call:

Code:
                CompilerParameters parms = new CompilerParameters(ScriptCompiler.GetReferenceAssemblies(), path, debug);
I get this error:
RunUO - [www.runuo.com] Version 2.1, Build 4152.19596
Core: Running on .NET Framework Version 4.0.30319
Core: Running with arguments: -debug
Core: Optimizing for 2 64-bit processors
Scripts: Compiling C# scripts... [Distros] done (0 errors, 0 warnings)
Scripts: Compiling C# scripts... [Modified] -skipped (empty)
Scripts: Compiling C# scripts... [Customs] failed (2 errors, 0 warnings)
Errors:
+ Customs/Misc/CC2.0.cs:
CS0234: Line 15: The type or namespace name 'Engines' does not exist in the
namespace 'Server' (are you missing an assembly reference?)
CS0246: Line 23: The type or namespace name 'PageEntry' could not be found (
are you missing a using directive or an assembly reference?)
+ Customs/WirtsLeg.cs:
CS0246: Line 47: The type or namespace name 'Moongate' could not be found (a
re you missing a using directive or an assembly reference?)
Scripts: One or more scripts failed to compile or no script files were found.
- Press return to exit, or R to try again.
when I have all of the correct using directives and stuff at top (if I move them to the distro folder, it will compile). Its becuase the regular "ScriptCompiler"s 'm_AdditionalReferences' is private, so I had to replicate it\make a new one in multi compiler. So its not getting the proper lower-level script stuff when the higher-level folders compile.
If I re-vert to an array, and attach the new references at the end like this:
Code:
                List<string> getRefAssemblies = new List<string>(ScriptCompiler.GetReferenceAssemblies());

                string[] refAssemblies = getRefAssemblies.ToArray();
                m_AdditionalReferences.ToArray().CopyTo(refAssemblies, (refAssemblies.Length - 1));

                CompilerParameters parms = new CompilerParameters(refAssemblies, path, debug);
I get this error:
RunUO - [www.runuo.com] Version 2.1, Build 4152.19793
Core: Running on .NET Framework Version 4.0.30319
Core: Optimizing for 2 64-bit processors
Scripts: Compiling C# scripts... [Distros] done (0 errors, 0 warnings)
Scripts: Compiling C# scripts... [Modified] -skipped (empty)
Scripts: Compiling C# scripts... [Customs] failed (2 errors, 0 warnings)
Errors:
+ Customs/WirtsLeg.cs:
CS0246: Line 8: The type or namespace name 'Item' could not be found (are yo
u missing a using directive or an assembly reference?)
CS0012: Line 47: The type 'Server.Item' is defined in an assembly that is no
t referenced. You must add a reference to assembly 'RunUO 2.0, Version=2.1.4152.
19793, Culture=neutral, PublicKeyToken=null'.
CS0246: Line 19: The type or namespace name 'Serial' could not be found (are
you missing a using directive or an assembly reference?)
CS0246: Line 24: The type or namespace name 'Mobile' could not be found (are
you missing a using directive or an assembly reference?)
CS0246: Line 32: The type or namespace name 'GenericWriter' could not be fou
nd (are you missing a using directive or an assembly reference?)
CS0246: Line 39: The type or namespace name 'GenericReader' could not be fou
nd (are you missing a using directive or an assembly reference?)
CS0246: Line 55: The type or namespace name 'Point3D' could not be found (ar
e you missing a using directive or an assembly reference?)
CS0246: Line 55: The type or namespace name 'Map' could not be found (are yo
u missing a using directive or an assembly reference?)
CS0246: Line 62: The type or namespace name 'Serial' could not be found (are
you missing a using directive or an assembly reference?)
CS0246: Line 67: The type or namespace name 'GenericWriter' could not be fou
nd (are you missing a using directive or an assembly reference?)
CS0246: Line 74: The type or namespace name 'GenericReader' could not be fou
nd (are you missing a using directive or an assembly reference?)
CS0246: Line 10: The type or namespace name 'Constructable' could not be fou
nd (are you missing a using directive or an assembly reference?)
CS0246: Line 10: The type or namespace name 'ConstructableAttribute' could n
ot be found (are you missing a using directive or an assembly reference?)
CS0246: Line 49: The type or namespace name 'Constructable' could not be fou
nd (are you missing a using directive or an assembly reference?)
CS0246: Line 49: The type or namespace name 'ConstructableAttribute' could n
ot be found (are you missing a using directive or an assembly reference?)
+ Customs/Misc/CC2.0.cs:
CS0246: Line 35: The type or namespace name 'SpeechEventArgs' could not be f
ound (are you missing a using directive or an assembly reference?)
CS0246: Line 103: The type or namespace name 'AccessLevel' could not be foun
d (are you missing a using directive or an assembly reference?)
Scripts: One or more scripts failed to compile or no script files were found.
- Press return to exit, or R to try again.
To get rid of all the errors, and to make it simpiler (imo), I just used a List<> to build the new refAssembiels, and then converted it to an array instead of working with the array:

Code:
                List<string> getRefAssemblies = new List<string>(ScriptCompiler.GetReferenceAssemblies());
                getRefAssemblies.AddRange(m_AdditionalReferences); //MultiCompiler references...

                string[] refAssemblies = getRefAssemblies.ToArray();

                //string[] refAssemblies = ScriptCompiler.GetReferenceAssemblies();
                //m_AdditionalReferences.ToArray().CopyTo(refAssemblies, (refAssemblies.Length - 1));

                CompilerParameters parms = new CompilerParameters(refAssemblies, path, debug);
which brings us back to the error of:
RunUO - [www.runuo.com] Version 2.1, Build 4152.19935
Core: Running on .NET Framework Version 4.0.30319
Core: Running with arguments: -debug
Core: Optimizing for 2 64-bit processors
Scripts: Compiling C# scripts... [Distros] done (0 errors, 0 warnings)
Scripts: Compiling C# scripts... [Modified] -skipped (empty)
Scripts: Compiling C# scripts... [Customs] done (0 errors, 0 warnings)
Scripts: Verifying...done (4582 items, 1060 mobiles) (1.42 seconds)
Error:
System.Reflection.TargetInvocationException: Exception has been thrown by the ta
rget of an invocation. ---> System.Exception: A poison with that level already e
xists.
at Server.Poison.Register(Poison reg)
at Server.PoisonImpl.Configure() in c:\Dropbox\Nagashizar 3.0\Server\Scripts\
Distros\Misc\Poison.cs:line 19
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Ob
ject target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAt
tributes, RuntimeType typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Obj
ect target, Object[] arguments, Signature sig, MethodAttributes methodAttributes
, RuntimeType typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisib
ilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture)
at Server.ScriptCompiler.Invoke(String method)
at Server.Core.Main(String[] args)
This exception is fatal, press return to exit
I know... I should of given all this info at the start... my bad :/

and just for easy info reference... here is the regular ScriptCompiler.cs (jic):

Code:
/***************************************************************************
 *                             ScriptCompiler.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : [email protected]
 *
 *   $Id: ScriptCompiler.cs 651 2010-12-28 09:24:08Z asayre $
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

#define Framework_4_0

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Security.Cryptography;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using System.Diagnostics;

namespace Server
{
    public static class ScriptCompiler
    {
        private static Assembly[] m_Assemblies;

        public static Assembly[] Assemblies
        {
            get
            {
                return m_Assemblies;
            }
            set
            {
                m_Assemblies = value;
            }
        }

        private static List<string> m_AdditionalReferences = new List<string>();

        public static string[] GetReferenceAssemblies()
        {
            List<string> list = new List<string>();

            string path = Path.Combine(Core.BaseDirectory, "Data/Assemblies.cfg");

            if (File.Exists(path))
            {
                using (StreamReader ip = new StreamReader(path))
                {
                    string line;

                    while ((line = ip.ReadLine()) != null)
                    {
                        if (line.Length > 0 && !line.StartsWith("#"))
                            list.Add(line);
                    }
                }
            }

            list.Add(Core.ExePath);

            list.AddRange(m_AdditionalReferences);

            return list.ToArray();
        }

        public static string GetDefines()
        {
            StringBuilder sb = null;

#if MONO
            AppendDefine( ref sb, "/d:MONO" );
#endif

            //These two defines are legacy, ie, depreciated.
            if (Core.Is64Bit)
                AppendDefine(ref sb, "/d:x64");

            AppendDefine(ref sb, "/d:Framework_2_0");

#if Framework_4_0
            AppendDefine(ref sb, "/d:Framework_4_0");
#endif

            return (sb == null ? null : sb.ToString());
        }

        public static void AppendDefine(ref StringBuilder sb, string define)
        {
            if (sb == null)
                sb = new StringBuilder();
            else
                sb.Append(' ');

            sb.Append(define);
        }

        private static byte[] GetHashCode(string compiledFile, string[] scriptFiles, bool debug)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bin = new BinaryWriter(ms))
                {
                    FileInfo fileInfo = new FileInfo(compiledFile);

                    bin.Write(fileInfo.LastWriteTimeUtc.Ticks);

                    foreach (string scriptFile in scriptFiles)
                    {
                        fileInfo = new FileInfo(scriptFile);

                        bin.Write(fileInfo.LastWriteTimeUtc.Ticks);
                    }

                    bin.Write(debug);
                    bin.Write(Core.Version.ToString());

                    ms.Position = 0;

                    using (SHA1 sha1 = SHA1.Create())
                    {
                        return sha1.ComputeHash(ms);
                    }
                }
            }
        }

        public static bool CompileCSScripts(out Assembly assembly)
        {
            return CompileCSScripts(false, true, out assembly);
        }

        public static bool CompileCSScripts(bool debug, out Assembly assembly)
        {
            return CompileCSScripts(debug, true, out assembly);
        }

        public static bool CompileCSScripts(bool debug, bool cache, out Assembly assembly)
        {
            Console.Write("Scripts: Compiling C# scripts...");
            string[] files = GetScripts("*.cs");

            if (files.Length == 0)
            {
                Console.WriteLine("no files found.");
                assembly = null;
                return true;
            }

            if (File.Exists("Scripts/Output/Scripts.CS.dll"))
            {
                if (cache && File.Exists("Scripts/Output/Scripts.CS.hash"))
                {
                    try
                    {
                        byte[] hashCode = GetHashCode("Scripts/Output/Scripts.CS.dll", files, debug);

                        using (FileStream fs = new FileStream("Scripts/Output/Scripts.CS.hash", FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            using (BinaryReader bin = new BinaryReader(fs))
                            {
                                byte[] bytes = bin.ReadBytes(hashCode.Length);

                                if (bytes.Length == hashCode.Length)
                                {
                                    bool valid = true;

                                    for (int i = 0; i < bytes.Length; ++i)
                                    {
                                        if (bytes[i] != hashCode[i])
                                        {
                                            valid = false;
                                            break;
                                        }
                                    }

                                    if (valid)
                                    {
                                        assembly = Assembly.LoadFrom("Scripts/Output/Scripts.CS.dll");

                                        if (!m_AdditionalReferences.Contains(assembly.Location))
                                        {
                                            m_AdditionalReferences.Add(assembly.Location);
                                        }

                                        Console.WriteLine("done (cached)");

                                        return true;
                                    }
                                }
                            }
                        }
                    }
                    catch
                    {
                    }
                }
            }

            DeleteFiles("Scripts.CS*.dll");

            using (CSharpCodeProvider provider = new CSharpCodeProvider())
            {
                string path = GetUnusedPath("Scripts.CS");

                CompilerParameters parms = new CompilerParameters(GetReferenceAssemblies(), path, debug);

                string defines = GetDefines();

                if (defines != null)
                    parms.CompilerOptions = defines;

                if (Core.HaltOnWarning)
                    parms.WarningLevel = 4;

#if !MONO
                CompilerResults results = provider.CompileAssemblyFromFile(parms, files);
#else
                parms.CompilerOptions = String.Format( "{0} /nowarn:169,219,414 /recurse:Scripts/*.cs", parms.CompilerOptions );
                CompilerResults results = provider.CompileAssemblyFromFile( parms, "" );
#endif
                m_AdditionalReferences.Add(path);

                Display(results);

#if !MONO
                if (results.Errors.Count > 0)
                {
                    assembly = null;
                    return false;
                }
#else
                if( results.Errors.Count > 0 ) {
                    foreach( CompilerError err in results.Errors ) {
                        if ( !err.IsWarning ) {
                            assembly = null;
                            return false;
                        }
                    }
                }
#endif
 
                if (cache && Path.GetFileName(path) == "Scripts.CS.dll")
                {
                    try
                    {
                        byte[] hashCode = GetHashCode(path, files, debug);

                        using (FileStream fs = new FileStream("Scripts/Output/Scripts.CS.hash", FileMode.Create, FileAccess.Write, FileShare.None))
                        {
                            using (BinaryWriter bin = new BinaryWriter(fs))
                            {
                                bin.Write(hashCode, 0, hashCode.Length);
                            }
                        }
                    }
                    catch
                    {
                    }
                }

                assembly = results.CompiledAssembly;
                return true;
            }
        }

        public static bool CompileVBScripts(out Assembly assembly)
        {
            return CompileVBScripts(false, out assembly);
        }

        public static bool CompileVBScripts(bool debug, out Assembly assembly)
        {
            return CompileVBScripts(debug, true, out assembly);
        }

        public static bool CompileVBScripts(bool debug, bool cache, out Assembly assembly)
        {
            Console.Write("Scripts: Compiling VB.NET scripts...");
            string[] files = GetScripts("*.vb");

            if (files.Length == 0)
            {
                Console.WriteLine("no files found.");
                assembly = null;
                return true;
            }

            if (File.Exists("Scripts/Output/Scripts.VB.dll"))
            {
                if (cache && File.Exists("Scripts/Output/Scripts.VB.hash"))
                {
                    byte[] hashCode = GetHashCode("Scripts/Output/Scripts.VB.dll", files, debug);

                    try
                    {
                        using (FileStream fs = new FileStream("Scripts/Output/Scripts.VB.hash", FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            using (BinaryReader bin = new BinaryReader(fs))
                            {
                                byte[] bytes = bin.ReadBytes(hashCode.Length);

                                if (bytes.Length == hashCode.Length)
                                {
                                    bool valid = true;

                                    for (int i = 0; i < bytes.Length; ++i)
                                    {
                                        if (bytes[i] != hashCode[i])
                                        {
                                            valid = false;
                                            break;
                                        }
                                    }

                                    if (valid)
                                    {
                                        assembly = Assembly.LoadFrom("Scripts/Output/Scripts.VB.dll");

                                        if (!m_AdditionalReferences.Contains(assembly.Location))
                                        {
                                            m_AdditionalReferences.Add(assembly.Location);
                                        }

                                        Console.WriteLine("done (cached)");

                                        return true;
                                    }
                                }
                            }
                        }
                    }
                    catch
                    {
                    }
                }
            }

            DeleteFiles("Scripts.VB*.dll");

            using (VBCodeProvider provider = new VBCodeProvider())
            {
                string path = GetUnusedPath("Scripts.VB");

                CompilerParameters parms = new CompilerParameters(GetReferenceAssemblies(), path, debug);

                string defines = GetDefines();

                if (defines != null)
                    parms.CompilerOptions = String.Format("/D:{0}", defines);

                if (Core.HaltOnWarning)
                    parms.WarningLevel = 4;

                CompilerResults results = provider.CompileAssemblyFromFile(parms, files);
                m_AdditionalReferences.Add(path);

                Display(results);

                if (results.Errors.Count > 0)
                {
                    assembly = null;
                    return false;
                }

                if (cache && Path.GetFileName(path) == "Scripts.VB.dll")
                {
                    try
                    {
                        byte[] hashCode = GetHashCode(path, files, debug);

                        using (FileStream fs = new FileStream("Scripts/Output/Scripts.VB.hash", FileMode.Create, FileAccess.Write, FileShare.None))
                        {
                            using (BinaryWriter bin = new BinaryWriter(fs))
                            {
                                bin.Write(hashCode, 0, hashCode.Length);
                            }
                        }
                    }
                    catch
                    {
                    }
                }

                assembly = results.CompiledAssembly;
                return true;
            }
        }

        public static void Display(CompilerResults results)
        {
            if (results.Errors.Count > 0)
            {
                Dictionary<string, List<CompilerError>> errors = new Dictionary<string, List<CompilerError>>(results.Errors.Count, StringComparer.OrdinalIgnoreCase);
                Dictionary<string, List<CompilerError>> warnings = new Dictionary<string, List<CompilerError>>(results.Errors.Count, StringComparer.OrdinalIgnoreCase);

                foreach (CompilerError e in results.Errors)
                {
                    string file = e.FileName;

                    // Ridiculous. FileName is null if the warning/error is internally generated in csc.
                    if (string.IsNullOrEmpty(file))
                    {
                        Console.WriteLine("ScriptCompiler: {0}: {1}", e.ErrorNumber, e.ErrorText);
                        continue;
                    }

                    Dictionary<string, List<CompilerError>> table = (e.IsWarning ? warnings : errors);

                    List<CompilerError> list = null;
                    table.TryGetValue(file, out list);

                    if (list == null)
                        table[file] = list = new List<CompilerError>();

                    list.Add(e);
                }

                if (errors.Count > 0)
                    Console.WriteLine("failed ({0} errors, {1} warnings)", errors.Count, warnings.Count);
                else
                    Console.WriteLine("done ({0} errors, {1} warnings)", errors.Count, warnings.Count);

                string scriptRoot = Path.GetFullPath(Path.Combine(Core.BaseDirectory, "Scripts" + Path.DirectorySeparatorChar));
                Uri scriptRootUri = new Uri(scriptRoot);

                Utility.PushColor(ConsoleColor.Yellow);

                if (warnings.Count > 0)
                    Console.WriteLine("Warnings:");

                foreach (KeyValuePair<string, List<CompilerError>> kvp in warnings)
                {
                    string fileName = kvp.Key;
                    List<CompilerError> list = kvp.Value;

                    string fullPath = Path.GetFullPath(fileName);
                    string usedPath = Uri.UnescapeDataString(scriptRootUri.MakeRelativeUri(new Uri(fullPath)).OriginalString);

                    Console.WriteLine(" + {0}:", usedPath);

                    Utility.PushColor(ConsoleColor.DarkYellow);

                    foreach (CompilerError e in list)
                        Console.WriteLine("    {0}: Line {1}: {3}", e.ErrorNumber, e.Line, e.Column, e.ErrorText);

                    Utility.PopColor();
                }

                Utility.PopColor();

                Utility.PushColor(ConsoleColor.Red);

                if (errors.Count > 0)
                    Console.WriteLine("Errors:");

                foreach (KeyValuePair<string, List<CompilerError>> kvp in errors)
                {
                    string fileName = kvp.Key;
                    List<CompilerError> list = kvp.Value;

                    string fullPath = Path.GetFullPath(fileName);
                    string usedPath = Uri.UnescapeDataString(scriptRootUri.MakeRelativeUri(new Uri(fullPath)).OriginalString);

                    Console.WriteLine(" + {0}:", usedPath);

                    Utility.PushColor(ConsoleColor.DarkRed);

                    foreach (CompilerError e in list)
                        Console.WriteLine("    {0}: Line {1}: {3}", e.ErrorNumber, e.Line, e.Column, e.ErrorText);

                    Utility.PopColor();
                }

                Utility.PopColor();
            }
            else
            {
                Console.WriteLine("done (0 errors, 0 warnings)");
            }
        }

        public static string GetUnusedPath(string name)
        {
            string path = Path.Combine(Core.BaseDirectory, String.Format("Scripts/Output/{0}.dll", name));

            for (int i = 2; File.Exists(path) && i <= 1000; ++i)
                path = Path.Combine(Core.BaseDirectory, String.Format("Scripts/Output/{0}.{1}.dll", name, i));

            return path;
        }

        public static void DeleteFiles(string mask)
        {
            try
            {
                string[] files = Directory.GetFiles(Path.Combine(Core.BaseDirectory, "Scripts/Output"), mask);

                foreach (string file in files)
                {
                    try { File.Delete(file); }
                    catch { }
                }
            }
            catch
            {
            }
        }

        private delegate CompilerResults Compiler(bool debug);

        public static bool Compile()
        {
            return Compile(false);
        }

        public static bool Compile(bool debug)
        {
            return Compile(debug, true);
        }

        public static bool Compile(bool debug, bool cache)
        {
            EnsureDirectory("Scripts/");
            EnsureDirectory("Scripts/Output/");

            if (m_AdditionalReferences.Count > 0)
                m_AdditionalReferences.Clear();

            List<Assembly> assemblies = new List<Assembly>();

            Assembly assembly;

            if (CompileCSScripts(debug, cache, out assembly))
            {
                if (assembly != null)
                {
                    assemblies.Add(assembly);
                }
            }
            else
            {
                return false;
            }

            if (Core.VBdotNet)
            {
                if (CompileVBScripts(debug, cache, out assembly))
                {
                    if (assembly != null)
                    {
                        assemblies.Add(assembly);
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                Console.WriteLine("Scripts: Skipping VB.NET Scripts...done (use -vb to enable)");
            }

            if (assemblies.Count == 0)
            {
                return false;
            }

            m_Assemblies = assemblies.ToArray();

            Console.Write("Scripts: Verifying...");

            Stopwatch watch = Stopwatch.StartNew();

            Core.VerifySerialization();

            watch.Stop();

            Console.WriteLine("done ({0} items, {1} mobiles) ({2:F2} seconds)", Core.ScriptItems, Core.ScriptMobiles, watch.Elapsed.TotalSeconds);

            return true;
        }

        public static void Invoke(string method)
        {
            List<MethodInfo> invoke = new List<MethodInfo>();

            for (int a = 0; a < m_Assemblies.Length; ++a)
            {
                Type[] types = m_Assemblies[a].GetTypes();

                for (int i = 0; i < types.Length; ++i)
                {
                    MethodInfo m = types[i].GetMethod(method, BindingFlags.Static | BindingFlags.Public);

                    if (m != null)
                        invoke.Add(m);
                }
            }

            invoke.Sort(new CallPriorityComparer());

            for (int i = 0; i < invoke.Count; ++i)
                invoke[i].Invoke(null, null);
        }

        private static Dictionary<Assembly, TypeCache> m_TypeCaches = new Dictionary<Assembly, TypeCache>();
        private static TypeCache m_NullCache;

        public static TypeCache GetTypeCache(Assembly asm)
        {
            if (asm == null)
            {
                if (m_NullCache == null)
                    m_NullCache = new TypeCache(null);

                return m_NullCache;
            }

            TypeCache c = null;
            m_TypeCaches.TryGetValue(asm, out c);

            if (c == null)
                m_TypeCaches[asm] = c = new TypeCache(asm);

            return c;
        }

        public static Type FindTypeByFullName(string fullName)
        {
            return FindTypeByFullName(fullName, true);
        }

        public static Type FindTypeByFullName(string fullName, bool ignoreCase)
        {
            Type type = null;

            for (int i = 0; type == null && i < m_Assemblies.Length; ++i)
                type = GetTypeCache(m_Assemblies[i]).GetTypeByFullName(fullName, ignoreCase);

            if (type == null)
                type = GetTypeCache(Core.Assembly).GetTypeByFullName(fullName, ignoreCase);

            return type;
        }

        public static Type FindTypeByName(string name)
        {
            return FindTypeByName(name, true);
        }

        public static Type FindTypeByName(string name, bool ignoreCase)
        {
            Type type = null;

            for (int i = 0; type == null && i < m_Assemblies.Length; ++i)
                type = GetTypeCache(m_Assemblies[i]).GetTypeByName(name, ignoreCase);

            if (type == null)
                type = GetTypeCache(Core.Assembly).GetTypeByName(name, ignoreCase);

            return type;
        }

        public static void EnsureDirectory(string dir)
        {
            string path = Path.Combine(Core.BaseDirectory, dir);

            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
        }

        public static string[] GetScripts(string filter)
        {
            List<string> list = new List<string>();

            GetScripts(list, Path.Combine(Core.BaseDirectory, "Scripts"), filter);

            return list.ToArray();
        }

        public static void GetScripts(List<string> list, string path, string filter)
        {
            foreach (string dir in Directory.GetDirectories(path))
                GetScripts(list, dir, filter);

            list.AddRange(Directory.GetFiles(path, filter));
        }
    }

    public class TypeCache
    {
        private Type[] m_Types;
        private TypeTable m_Names, m_FullNames;

        public Type[] Types { get { return m_Types; } }
        public TypeTable Names { get { return m_Names; } }
        public TypeTable FullNames { get { return m_FullNames; } }

        public Type GetTypeByName(string name, bool ignoreCase)
        {
            return m_Names.Get(name, ignoreCase);
        }

        public Type GetTypeByFullName(string fullName, bool ignoreCase)
        {
            return m_FullNames.Get(fullName, ignoreCase);
        }

        public TypeCache(Assembly asm)
        {
            if (asm == null)
                m_Types = Type.EmptyTypes;
            else
                m_Types = asm.GetTypes();

            m_Names = new TypeTable(m_Types.Length);
            m_FullNames = new TypeTable(m_Types.Length);

            Type typeofTypeAliasAttribute = typeof(TypeAliasAttribute);

            for (int i = 0; i < m_Types.Length; ++i)
            {
                Type type = m_Types[i];

                m_Names.Add(type.Name, type);
                m_FullNames.Add(type.FullName, type);

                if (type.IsDefined(typeofTypeAliasAttribute, false))
                {
                    object[] attrs = type.GetCustomAttributes(typeofTypeAliasAttribute, false);

                    if (attrs != null && attrs.Length > 0)
                    {
                        TypeAliasAttribute attr = attrs[0] as TypeAliasAttribute;

                        if (attr != null)
                        {
                            for (int j = 0; j < attr.Aliases.Length; ++j)
                                m_FullNames.Add(attr.Aliases[j], type);
                        }
                    }
                }
            }
        }
    }

    public class TypeTable
    {
        private Dictionary<string, Type> m_Sensitive, m_Insensitive;

        public void Add(string key, Type type)
        {
            m_Sensitive[key] = type;
            m_Insensitive[key] = type;
        }

        public Type Get(string key, bool ignoreCase)
        {
            Type t = null;

            if (ignoreCase)
                m_Insensitive.TryGetValue(key, out t);
            else
                m_Sensitive.TryGetValue(key, out t);

            return t;
        }

        public TypeTable(int capacity)
        {
            m_Sensitive = new Dictionary<string, Type>(capacity);
            m_Insensitive = new Dictionary<string, Type>(capacity, StringComparer.OrdinalIgnoreCase);
        }
    }
}
 

Lokai

Knight
If you are still having issues with this, I just successfully added my Main.cs and ScriptCompiler.cs mods to the latest SVN. Works great, no issues.
 

Vorspire

Knight
Umm, I wrote a dynamic custom compiler for WebServerLite AGES ago... :D

http://webserverlite.codeplex.com/SourceControl/changeset/view/39723#466998

Usage: Access the CSC (as example):
Code:
		private static void CompileCustomFiles()
		{
			CSharpCompiler csc = new CSharpCompiler("CustomFiles.dll");

			csc.AddReference("WebServer.dll");
			csc.AddReference("AspNetHost.dll");
			csc.AddFileMask("*.ws.cs");
			csc.AddFileMask("*.wsobj.cs");
			csc.AddFileMask("*.wsmod.cs");

			Core.ConsoleWriteLine("Compiling custom C# files...");

			csc.Compile();

			Core.ConsoleWriteLine(csc.StatusMessage);

			if (csc.Errors.Length > 0)
			{
				for (int i = 0; i < csc.Errors.Length; i++)
					Core.ConsoleWriteError(csc.Errors[i]);

				Core.WaitForKeyPress("Press any key to continue.");
			}
			else if (csc.Results != null)
			{
				_CustomAssembly = csc.Results.CompiledAssembly;
			}
		}

Want to use this for multiple compilations?
Just construct CSharpCompiler with a different output file-name argument and load from a different source dir.
- Of course it's pretty basic and doesn't have anything to do with the RunUO API, but you can use it as a base I guess.
 

Peoharen

Sorceror
Unfortunately, RunUO doesn't quote support tacked on libraries so that won't help much Vor. Example, code an item and compile it solely into a library then try to use it. Even coding a command seems to fail out. Of course, I was using C++ compiled using .NET's compiler so I could be wrong in the long run, but it didn't work for me. You can of course code various utilities and tools for use.

Secondly, Lokai's workbench, is well. Lokai's. Not saying he is terrible but all he seems to do is copypaste old releases and slap his name on them. And sometimes things get a little absurd. The primary problem with the so called workbench mods is how RunUO saves. Once you compile your workbanch6 or w/e and save. You must always compile workbench6 or your saves will crash out. Which in makes the concept of even three folders a little silly since you must always compile them. Progression in library count can even lead to a point where your script validation time exceeds most people's server load up to running times.

You should flat out compile your single workfolder each and every time you reload. When stuff is stable, move it to your main folder and recompile the primary library. I suggest making it part of your sever maintenance to do such.
 
Secondly, Lokai's workbench, is well. Lokai's. Not saying he is terrible but all he seems to do is copypaste old releases and slap his name on them.
...
He took a proposed Idea and turned it into code

He may have improved on many scripts and published them, but he has a lot of original content as well. The members of this forum are at varying levels of skill, talent and originality, It's not a good idea to go around bashing other developers, no matter how subtle you think you are being.

Besides, as far as originality goes technically we are all riding on the coattails of krrios/Asayre/Jeff/Zippy and even Richard Garriot

EDIT: forgot Mark. And psz, and thedemise bug squashers (dont know all their names)

The primary problem with the so called workbench mods is how RunUO saves. Once you compile your workbanch6 or w/e and save. You must always compile workbench6 or your saves will crash out. Which in makes the concept of even three folders a little silly since you must always compile them. Progression in library count can even lead to a point where your script validation time exceeds most people's server load up to running times.

You don't generally save when you are testing a script, especially if its volatile enough to be in "workbench 6"
 

Peoharen

Sorceror
...He took a proposed Idea and turned it into code
You don't generally save when you are testing a script, especially if its volatile enough to be in "workbench 6"
And I didn't say it was terrible. You're right on 90% of the user base here needs someone to update scripts for them. However, I was addressing the concept of voluntarily compiling additional folders cannot be done due to how saving is done and like wise multiple to compile in libraries can in fact take you backwards in load time. Use one, and always compile it.

As for not saving while testing. I don't understand that. For copypaste artifacts, yeah you wouldn't save. But you don't need to test those outside of compiling really. If for example you edited FS's Taming Level System, why wouldn't you save the world during the pet training and testing? What if you simply added code to Serialization/Deserialization? You need to save and test that as well, if you pasted the wrong order of things in deserialize and put it on a live shard you could lose days worth of information by the time you realize your code is faulty.

Edit: Also, not to get into a dick waving event, since we're sharing links and discussing where we thought what came from where. Here is a full release of a workbench mod released a year before Lokais: http://www.runuo.com/community/threads/load-scripts-from-a-library.47083/
 

Pure Insanity

Sorceror
Yeah, it has been done by 3 different people. At least that I know about...and pretty much each one is different in it's own way.

As for the whole issue with saving...it all depends on the system that is being loaded from the separate folder. You can serialize/deserialize any object you want, your own way. You don't have to use runuo's method to serialize your item/class if you don't want to. You can save it as a binary file, xml file, hell even mysql if you really want. Thus allowing you to control where you want the save file to be kept and how you wish to keep it.

Ultimately, this is a very handy system. Which is why it has been done so many times, by different people. It's very handy when used properly, can save load time a lot. And also makes it easy to drop a system in and out if you need to. Really it just depends on the server type and the person working on the server, and what their needs are. I know it can be a pain to wait for a minute or longer for a restart on a large server, and something like this could really help. It could allow you to create your own separate systems in one of the folders, and keep from having to compile everything else when you restart for something related to the system you're working on.

And RunUO supports libraries just fine...but anyways, this is getting a bit off topic. So I'll go now. D:
 

Vorspire

Knight
The reason my CSharpCompiler doesn't work the way you'd expect is because you *probably* didn't initialize it with a reference to RunUO.exe or Scripts.cs.dll - you do this before starting the compile process and you won't have any problems at all.

Code:
private static Assembly _CustomAssembly;

private static void CompileCustomFiles()
{
      CSharpCompiler csc = new CSharpCompiler("CustomFiles.dll");

      csc.AddReference("RunUO.exe");
      csc.AddReference("Scripts/Output/Scripts.cs.dll");
      csc.AddFileMask("*.cs");

      Console.WriteLine("Compiling custom C# files...");

      csc.Compile();

      Console.WriteLine(csc.StatusMessage);

      if (csc.Errors.Length > 0)
      {
            for (int i = 0; i < csc.Errors.Length; i++)
            { Console.WriteLine(csc.Errors[i]); }
      }
      else if (csc.Results != null)
      { _CustomAssembly = csc.Results.CompiledAssembly; }
}

You will *of course* have to add "CustomFiles.dll" to the Assemblies.cfg in the Data folder for it to be used.
 

Ki Frost

Sorceror
I know it's been a while, but I did get this working... I just have a bad habit of remembering to go back and post my solutions/findings.

I know I said I didn't want to modify any files, and for the most part I didn't, but the only thing that was wrong with the multi-compiler was the call to the Invoke method of ScriptCompiler.

Code:
        public static void Invoke(string method)

        {

            List<MethodInfo> invoke = new List<MethodInfo>();

            for (int a = 0; a < m_Assemblies.Length; ++a)

            {

                Type[] types = m_Assemblies[a].GetTypes();

                for (int i = 0; i < types.Length; ++i)

                {

                    MethodInfo m = types[i].GetMethod(method, BindingFlags.Static | BindingFlags.Public);

                    if (m != null)

                        invoke.Add(m);

                }

            }

            invoke.Sort(new CallPriorityComparer());

            for (int i = 0; i < invoke.Count; ++i)

                invoke[i].Invoke(null, null);

        }
to
Code:
        public static void Invoke(string method)
        {
            List<MethodInfo> invoke = new List<MethodInfo>();

            for (int a = 0; a < m_Assemblies.Length; ++a)
            {
                Type[] types = m_Assemblies[a].GetTypes();

                for (int i = 0; i < types.Length; ++i)
                {
                    MethodInfo m = types[i].GetMethod(method, BindingFlags.Static | BindingFlags.Public);

                    if (m != null && !invoke.Contains(m))
                        invoke.Add(m);
                }
            }

            invoke.Sort(new CallPriorityComparer());

            for (int i = 0; i < invoke.Count; ++i)
                invoke[i].Invoke(null, null);
        }
Specifically: The
Code:
                    if (m != null && !invoke.Contains(m))
                        invoke.Add(m);
Which I think could be classified as a bug, but since it will only ever be triggered by multiple assembly reference solutions, it will probably never be hit with a standard version.

Installation: Besides the Fix/Edit in the Invoke method of ScriptCompiler.cs, all you need to do is go into the Main.cs, around line 480 and change the
while (!ScriptCompiler.Compile(m_Debug, m_Cache))
to
while (!MultiCompiler.Compile(m_Debug, m_Cache))

NOTE:
I made this with SVN 669 and have NO idea how it will react with any of the other publishes...
 

Attachments

  • MultiCompiler.cs
    10.2 KB · Views: 3
Top