//--------------------------------------------------------------------------------------------------------------------------------------------------- // CompilerControl class, inherits from UserControl // The code behind the CompilerControl form // Builds the form for the student input and code output. Handles loading challenges from XML, calls compiling and running code // (CSharpCompiler), handles form items (scrolling, adding line numbers, buttons, resizing, etc.) and docking/undocking of the window. //--------------------------------------------------------------------------------------------------------------------------------------------------- // Engineered by: Amanda Chaffin //--------------------------------------------------------------------------------------------------------------------------------------------------- namespace DarkWynter.App { #region Using Statements using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; using System.CodeDom.Compiler; using System.CodeDom; using System.IO; using System.Xml; using Microsoft.Xna; using Microsoft.Xna.Framework; using DarkWynter.Stream; using DarkWynter.Engine.Globals; using DarkWynter.Engine.Init; using DarkWynter.Engine.Utilities; using DarkWynter.Engine.EventControl; using DarkWynter.Game; using DarkWynter.Engine.GameObjects; using DarkWynter.App.Surveys; using DarkWynter.App.ChallengeSet; using System.Threading; using CSTextEditor; #endregion /// /// The compiler form /// public partial class CompilerControl : UserControl { public static CompilerControl compilerControl; public static List challengeList; public static MenuStrip menuStripPointer; public static RichTextBox codeWindow_RTB; public static string customErrorMessage; public static string studentCode; public delegate void Output_EventHandler(string message); CompilerResults compilerResults; PrettyCompilerControl prettyCompilerControl; int unsuccessfulCompiles = 0; string cleanedStudentCode; bool docked = true; /// /// Creates a new compiler control (form), initializes, and displays /// public CompilerControl() { InitializeComponent(); this.Dock = DockStyle.Fill; LoadChallenges(); menuStripPointer = compiler_MS; CompilerControl.codeWindow_RTB = this.code_TB; Engine.DarkWynterEngine.onLevelChange += new DarkWynter.Engine.DarkWynterEngine.OnLevelChange(this.Load_OnLevelChange); compilerControl = this; number_L.Font = new Font(code_TB.Font.FontFamily, code_TB.Font.Size - 0.0f); } /// /// Resets the text to the next challenge, the delegate starts the level change sequence /// /// Controller boolean event arguments public void Load_OnLevelChange(EventArgs e) { Statics_Engine.GameSettings.isProblemFinished = true; resetText(); } /// /// Loads the challenges from XML /// private void LoadChallenges() { #region Challenges // Create Challenge List challengeList = new List(); for (int i = 0; i < XML.ChallengeNodes.Count; i++) { if (int.Parse(XML.ChallengeNodes[i].Attributes["id"].Value) == 0) { challengeList.Add(new Challenge0(XML.ChallengeNodes[i])); } if (int.Parse(XML.ChallengeNodes[i].Attributes["id"].Value) == 1) { challengeList.Add(new Challenge1(XML.ChallengeNodes[i])); } if (int.Parse(XML.ChallengeNodes[i].Attributes["id"].Value) == 2) { challengeList.Add(new Challenge2(XML.ChallengeNodes[i])); } } // Init first problem Statics_Engine.GameSettings.currentChallengeNumber = DarkWynter.Engine.Globals.Statics_Engine.levelIndex; // Load the first problem code_TB.Text = challengeList[Statics_Engine.GameSettings.currentChallengeNumber].GetScaffolding(); // Load the first instructions InstructionControl.dialoguePointer_TB.Text += challengeList[Statics_Engine.GameSettings.currentChallengeNumber].challengeDialogue[challengeList[Statics_Engine.GameSettings.currentChallengeNumber].dialogueCounter] + "\r\n"; #endregion } #region Compile and Run the code /// /// Compile the code and provide output /// /// String output of the program, stores in output text box public string CompileCodeWithOutput() { CompileCode(); return output_TB.Text; } /// /// Compiles the code in the codeTextBox.Text field /// public void CompileCode() { Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Button Message] Student hit compiler button [End Button Message]"); Logging.G2LLogList.Add("\r\n[Student Code] - \r\n"); Logging.G2LLogList.Add(cleanedStudentCode + "\r\n[End Student Code]\r\n"); compilerResults = DarkWynter.Engine.Compiler.CSharpCompiler.CompileCode(studentCode); // If _does_not_ compile if (compilerResults.Errors.Count > 0) { // Display compilation errors. StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendFormat("Errors building {0} into {1}\n\n", studentCode, compilerResults.PathToAssembly); foreach (CompilerError ce in compilerResults.Errors) { stringBuilder.Append(ce.ToString()); stringBuilder.Append("\n"); } unsuccessfulCompiles++; GameEventHandler.CurrentGameConditions.triggerSanity = 11; output_TB.Text = stringBuilder.ToString(); Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Error Message] There were errors in the code [End Error Message] "); Logging.G2LLogList.Add(stringBuilder.ToString()); Logging.G2LLogList.Add(" [Record Message] This is the " + unsuccessfulCompiles + " attempt at this problem. [End Record Message] "); } if (compilerResults.Errors.Count == 0) { output_TB.Text = "Compile Successful\n"; Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Success Message] There were no errors in the code [End Success Message] "); } } /// /// Runs the compiled code /// /// /// private void runMenuItem_TS_Click(object sender, EventArgs e) { RunCode(); } /// /// Runs the Code, after compilation, in the codeTextBoc.Text field /// public void RunCode() { // If it hasn't been compiled, ask the students to compile it if (compilerResults == null) { output_TB.Text = "You must compile before you can run!"; Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Error Message] Student tried to run without compiling first. [End Error Message]"); return; } // If compiles, validate output if (compilerResults.Errors.Count == 0) { StringWriter stringWriter = new StringWriter(); Console.SetOut(stringWriter); // Execute user code DarkWynter.Engine.Compiler.CSharpCompiler.Execute(compilerResults.CompiledAssembly, "StudentCode", "StudentCode", null); // List output in compiler feedback box try { output_TB.Text += stringWriter.ToString(); Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Compiler output] - "); Logging.G2LLogList.Add(stringWriter.ToString() + "[End Compiler output]\r\n"); // Execute instructor evaluation and feedback function if (challengeList[Statics_Engine.GameSettings.currentChallengeNumber].ValidateStudentCode(studentCode, stringWriter.ToString())) { if (Statics_Engine.GameSettings.isProblemFinished == false) { // If correct, call the vis code challengeList[Statics_Engine.GameSettings.currentChallengeNumber].RunVizualization(); Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Success Message] Bridge " + Statics_Engine.GameSettings.currentChallengeNumber + " built successfully. [End Success Message]"); // Ensure Current Prob does not go out of bounds if (Statics_Engine.GameSettings.currentChallengeNumber < challengeList.Count - 1) { // Finish the vis and don't run it again Statics_Engine.GameSettings.isProblemFinished = true; } } } // write the custom error message to the screen and to the log else output_TB.Text = customErrorMessage; Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Custom Error Message] " + customErrorMessage + " [End Custom Error Message]"); } catch (NullReferenceException nre) { output_TB.Text = nre.ToString(); Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Compiler output] - "); } // Move the scroller down scroll2Text(); stringWriter.Close(); } } #endregion #region Buttons, buttons, and more buttons /// /// Open the form, deactivates Engine controls, resize the gameWindow, handle resetText() trigger /// /// /// private void openMenuItem_TS_Click(object sender, EventArgs e) { Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Button Message] Compiler panels opened [End Button Message]"); Logging.G2LLogList.Add("\r\n[Game Message] Student is on problem number " + Statics_Engine.GameSettings.currentChallengeNumber); Logging.G2LLogList.Add("Student is on island number " + Statics_Engine.GameSettings.currentChallengeNumber + " [End Game Message] \r\n"); Engine.DarkWynterEngine.Deactivate(); if (Statics_Engine.PlayerSettings.doResetText == true) { resetText(); } // Set the viewport Microsoft.Xna.Framework.Graphics.Viewport viewport = new Microsoft.Xna.Framework.Graphics.Viewport(); viewport.MinDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MinDepth; viewport.MaxDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MaxDepth; Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport = viewport; } /// /// Reactivates Engine controls, resets gameWindow /// /// /// private void closeMenuItem_TS_Click(object sender, EventArgs e) { Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Button Message] Compiler panels closed [End Button Message] "); Engine.DarkWynterEngine.Activate(); // Set the viewport Microsoft.Xna.Framework.Graphics.Viewport viewport = new Microsoft.Xna.Framework.Graphics.Viewport(); viewport.MinDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MinDepth; viewport.MaxDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MaxDepth; Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport = viewport; } /// /// Will save out to a text file when I finish it /// /// /// private void save_TS_Click(object sender, EventArgs e) { // save the code out String name = "problem" + Statics_Engine.GameSettings.currentChallengeNumber + ".txt"; string logFile = "../../../__Game/Compiler/Logs/" + name; StreamWriter myFile = null; FileStream fileStream = File.Open(logFile, FileMode.CreateNew, FileAccess.Write); myFile = new StreamWriter(fileStream); myFile.WriteLine(DateTime.Now); myFile.Write(code_TB.Text); myFile.Close(); } /// /// Activate Compiler /// /// /// private void compileMenuItem_TS_Click(object sender, EventArgs e) { // Compile student code studentCode = code_TB.Text; cleanedStudentCode = studentCode.Trim(); cleanedStudentCode = studentCode.Replace('\n', ' '); CompileCode(); } /// /// Refreshes to the next code set /// /// /// public void refreshMenuItem_TS_Click(object sender, EventArgs e) { if (Statics_Engine.GameSettings.isProblemFinished == true) { //HERE Statics_Engine.GameSettings.currentChallengeNumber++; resetText(); resetText(); } else resetText(); } #endregion #region Helper methods /// /// Autoscoll boxNumber keeps track of which one we are changing /// public void scroll2Text() { output_TB.SelectionStart = output_TB.Text.Length; output_TB.ScrollToCaret(); output_TB.Select(); } /// /// Resets all the text boxes back to initial state /// public void resetText() { // Reset dialog counter in problem if (Statics_Engine.GameSettings.currentChallengeNumber != -1) { challengeList[Statics_Engine.GameSettings.currentChallengeNumber].dialogueCounter = 0; // Clear boxes InstructionControl.dialoguePointer_TB.Text = ""; output_TB.Text = ""; code_TB.Text = ""; // Load the problem code_TB.Text = challengeList[Statics_Engine.GameSettings.currentChallengeNumber].GetScaffolding(); // Load the first instructions InstructionControl.dialoguePointer_TB.Text += challengeList[Statics_Engine.GameSettings.currentChallengeNumber] .challengeDialogue[challengeList[Statics_Engine.GameSettings.currentChallengeNumber].dialogueCounter] + "\r\n"; Statics_Engine.GameSettings.isProblemFinished = false; } } private void updateNumber_L() { //we get index of first visible char and number of first visible line System.Drawing.Point pos = new System.Drawing.Point(0, 0); int firstIndex = code_TB.GetCharIndexFromPosition(pos); int firstLine = code_TB.GetLineFromCharIndex(firstIndex); //now we get index of last visible char and number of last visible line pos.X = ClientRectangle.Width; pos.Y = ClientRectangle.Height; int lastIndex = code_TB.GetCharIndexFromPosition(pos); int lastLine = code_TB.GetLineFromCharIndex(lastIndex); //this is point position of last visible char, we'll use its Y value for calculating numberLabel size pos = code_TB.GetPositionFromCharIndex(lastIndex); //finally, renumber label number_L.Text = ""; for (int i = firstLine; i <= lastLine + 1; i++) { number_L.Text += i + 1 + "\n"; } } private void code_TB_TextChanged(object sender, EventArgs e) { updateNumber_L(); } private void code_TB_VScroll(object sender, EventArgs e) { //move location of numberLabel for amount of pixels caused by scrollbar int d = code_TB.GetPositionFromCharIndex(0).Y % (code_TB.Font.Height + 1); number_L.Location = new System.Drawing.Point(0, d); updateNumber_L(); } private void code_TB_Resize(object sender, EventArgs e) { code_TB_VScroll(null, null); } private void code_TB_FontChanged(object sender, EventArgs e) { updateNumber_L(); code_TB_VScroll(null, null); } #endregion #region External Compiler /// /// Allows the compiler to run on a seperate thread /// /// Sender controller /// Controller boolean event arguments private void cSCompilerMenuItem_TS_Click(object sender, EventArgs e) { // Form thread Thread t = new Thread(new ThreadStart(compilerForm)); t.SetApartmentState(ApartmentState.STA); t.Start(); } /// /// Initialize controller form /// private void compilerForm() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); prettyCompilerControl = new PrettyCompilerControl(); prettyCompilerControl.textEditorControl1.Text = challengeList[Statics_Engine.GameSettings.currentChallengeNumber].GetScaffolding(); Application.Run(prettyCompilerControl); } /// /// Allows compiler form to be docked/undocked into/from game window /// /// Sender controller /// Controller boolean event arguments private void undockMenuItem_TS_Click(object sender, EventArgs e) { if (docked) { UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Controls.Clear(); UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Controls.Add(this); UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Name = "CompilerForm"; UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Text = "Compiler Form"; UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.WindowState = FormWindowState.Maximized; UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.ControlBox = false; UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.MaximizeBox = false; UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.MinimizeBox = false; UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.ResumeLayout(false); UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Show(); UCADInterfaceControl._UCAD_Inteface.LeftPanelCollapse(); undockMenuItem_TS.Text = "Dock"; docked = false; } else { UCADInterfaceControl._UCAD_Inteface.UCAD_Left_Panel.Controls.Clear(); UCADInterfaceControl._UCAD_Inteface.UCAD_Left_Panel.Controls.Add(this); UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Hide(); UCADInterfaceControl._UCAD_Inteface.LeftPanelExpand(); undockMenuItem_TS.Text = "Undock"; docked = true; } } #endregion } }