/// <summary>
 ///     Replaces CompiledData instance.
 /// </summary>
 public void ReplaceCompiledData(CompiledData cd)
 {
     lock (_locker)
     {
         _compiledDataToBeReplaced = cd;
     }
 }
        /// <summary>
        ///     Compiles game class code.
        /// </summary>
        private void CompileInternal(string code, bool saveState)
        {
            var linesOffset = Header.Length;

            var compiledSuccessfully = false;
            var compilationErrorOnLine = -1;
            string compilationErrorText = null;

            string finalCode =
                $"{string.Join("\n", Header)}\n{code}\n{string.Join("\n", Footer)}\n{string.Join("\n", WpfWrapper)}";

            var results = _compiler.CompileAssemblyFromSource(_compilerParameters,
                finalCode);

            if (results.Errors.Count == 0)
            {
                var asem = results.CompiledAssembly;

                var types = asem.GetTypes();
                Type type = null;
                for (var i = 0; i < types.Length; ++i)
                {
                    var attrs = types[i].CustomAttributes;
                    foreach (var attr in attrs)
                    {
                        if (attr.AttributeType.Name == "MainClass")
                        {
                            type = types[i];
                            break;
                        }
                    }
                }

                if (type != null)
                {
                    var inst = Activator.CreateInstance(type);
                    var fs = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                    var compiledData = new CompiledData
                    {
                        Instance = inst,
                        Fields = fs,
                        NeedToSaveState = saveState,
                        DrawTrackDelegates = new List<Action<DrawingContext>>()
                    };

                    var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                    for (var i = 0; i < methods.Length; ++i)
                    {
                        foreach (var attr in methods[i].CustomAttributes)
                        {
                            if (attr.AttributeType.Name == "InitMethod" && IsInitMethodSignature(methods[i]))
                                compiledData.InitDelegate =
                                    (Action) Delegate.CreateDelegate(typeof (Action), inst, methods[i]);
                            if (attr.AttributeType.Name == "DrawMethod" && IsDrawMethodSignature(methods[i]))
                                compiledData.DrawDelegate =
                                    (Action<DrawingContext>)
                                        Delegate.CreateDelegate(typeof (Action<DrawingContext>), inst, methods[i]);
                            if (attr.AttributeType.Name == "DrawTrackMethod" && IsDrawTrackMethodSignature(methods[i]))
                                compiledData.DrawTrackDelegates.Add((Action<DrawingContext>)
                                    Delegate.CreateDelegate(typeof (Action<DrawingContext>), inst, methods[i]));
                            if (attr.AttributeType.Name == "TickMethod" && IsTickMethodSignature(methods[i]))
                                compiledData.TickDelegate =
                                    (Action<double, Dictionary<char, bool>>)
                                        Delegate.CreateDelegate(typeof (Action<double, Dictionary<char, bool>>), inst,
                                            methods[i]);
                        }
                    }

                    compiledSuccessfully = true;
                    if (Compiled != null)
                    {
                        var tmpCompiled = Compiled;
                        _synchronizationContext.Send(
                            o => tmpCompiled(this, new CompiledEventArgs(compiledData)), null);
                    }
                }
            }
            else
            {
                compilationErrorOnLine = results.Errors[0].Line - linesOffset;
                compilationErrorText = results.Errors[0].ErrorText;
            }

            if (!compiledSuccessfully && CompilationError != null)
            {
                _synchronizationContext.Send(
                    o =>
                        CompilationError(this,
                            new CompilationErrorEventArgs(compilationErrorOnLine, compilationErrorText)), null);
            }

            lock (_locker)
            {
                _compilingTask = null;
                if (_pendingCode != null)
                {
                    var tmp = string.Copy(_pendingCode);
                    _compilingTask = Task.Factory.StartNew(() => CompileInternal(tmp, _saveState));
                    _pendingCode = null;
                }
            }
        }
 public CompiledEventArgs(CompiledData compiledData)
 {
     CompiledData = compiledData;
 }
        /// <summary>
        ///     Game tick method.
        /// </summary>
        private void GameTick()
        {
            var dtStopwatch = new Stopwatch();
            var gameTickStopwatch = new Stopwatch();
            var fieldsChangedStopwacth = new Stopwatch();
            var immediateFieldsChanged = false;

            uint ii = 0;
            fieldsChangedStopwacth.Start();
            while (!_evExit.WaitOne(SettingsForm.Instance.DesiredDt))
            {
                // copy values to local variables in case they change during game tick
                bool justPausedLocal;
                bool pausedLocal;
                bool needToSimulateTimelapseSceneLocal;
                Dictionary<char, bool> inputLocal;
                CompiledData compiledDataToBeReplacedLocal;
                int currentTrackBarValueLocal;
                lock (_locker)
                {
                    justPausedLocal = _justPaused;
                    _justPaused = false;
                    pausedLocal = Paused;
                    needToSimulateTimelapseSceneLocal = _needToSimulateTimelapseScene;
                    _needToSimulateTimelapseScene = false;
                    inputLocal = new Dictionary<char, bool>(_input);
                    compiledDataToBeReplacedLocal = _compiledDataToBeReplaced;
                    _compiledDataToBeReplaced = null;
                    currentTrackBarValueLocal = CurrentTrackBarValue;
                }
                gameTickStopwatch.Restart();

                // react to StoreLastFrames value change in pause mode
                if (pausedLocal && _dynamicGameSimulator.Snapshots.Count != SettingsForm.Instance.StoreLastFrames)
                {
                    if (_dynamicGameSimulator.Snapshots.Count > SettingsForm.Instance.StoreLastFrames)
                    {
                        var count = _dynamicGameSimulator.Snapshots.Count - SettingsForm.Instance.StoreLastFrames;
                        _dynamicGameSimulator.RemoveSnapshotsFromEnd(count);
                        currentTrackBarValueLocal = Math.Min(currentTrackBarValueLocal,
                            _dynamicGameSimulator.Snapshots.Count);
                        CurrentTrackBarValue = currentTrackBarValueLocal;
                    }
                    else
                    {
                        var count = SettingsForm.Instance.StoreLastFrames - _dynamicGameSimulator.Snapshots.Count;
                        if (justPausedLocal)
                        {
                            currentTrackBarValueLocal = _dynamicGameSimulator.Snapshots.Count;
                            CurrentTrackBarValue = currentTrackBarValueLocal;
                        }
                        _dynamicGameSimulator.AddDummySnapshots(count);
                        _dynamicGameSimulator.SimulateGame(_dynamicGameSimulator.Snapshots.Count - count - 1);
                        if (justPausedLocal)
                        {
                            CurrentTrackBarValueChanged?.Invoke(this, EventArgs.Empty);
                        }
                    }
                    GraphicsControl.Dispatcher.Invoke(_dynamicGameSimulator.RenderTimelapseScene);
                }

                // perform hot code swapping
                if (compiledDataToBeReplacedLocal != null)
                {
                    var canGoOn = true;
                    if (SettingsForm.Instance.CheckInfiniteLoops)
                    {
                        var state = compiledDataToBeReplacedLocal.DumpGameState();
                        canGoOn = compiledDataToBeReplacedLocal.CheckWhileTrue(16, inputLocal);
                        compiledDataToBeReplacedLocal.SetGameState(state);
                    }
                    if (canGoOn)
                    {
                        if (compiledDataToBeReplacedLocal.NeedToSaveState)
                        {
                            var state = CompiledData.DumpGameState();
                            CompiledData = compiledDataToBeReplacedLocal;
                            CompiledData.SetGameState(state);
                        }
                        else
                        {
                            CompiledData = compiledDataToBeReplacedLocal;
                            CompiledData.TryInvokeInitDelegate();
                            Paused = false;
                            pausedLocal = false;
                            needToSimulateTimelapseSceneLocal = false;
                            _dynamicGameSimulator.RemoveSnapshotsFromEnd(_dynamicGameSimulator.Snapshots.Count);
                            PausedChanged?.Invoke(this, EventArgs.Empty);
                        }
                    }
                }

                // simulate game
                if (needToSimulateTimelapseSceneLocal)
                {
                    _dynamicGameSimulator.SimulateGame(currentTrackBarValueLocal - 1);
                }

                // call move delegate
                dtStopwatch.Stop();
                var dt = 1000.0*dtStopwatch.ElapsedTicks/Stopwatch.Frequency;
                var b = currentTrackBarValueLocal < _dynamicGameSimulator.Snapshots.Count && !pausedLocal;
                MoveScene(b ? _dynamicGameSimulator.Snapshots[currentTrackBarValueLocal].Dt : dt,
                    b && SettingsForm.Instance.UseTrackedInput
                        ? _dynamicGameSimulator.Snapshots[currentTrackBarValueLocal].Input
                        : inputLocal, currentTrackBarValueLocal - 1);
                dtStopwatch.Restart();

                // take snapshot
                if (!pausedLocal)
                {
                    if (currentTrackBarValueLocal < _dynamicGameSimulator.Snapshots.Count)
                    {
                        CurrentTrackBarValue = currentTrackBarValueLocal + 1;
                        CurrentTrackBarValueChanged?.Invoke(this, EventArgs.Empty);
                    }
                    else
                    {
                        _dynamicGameSimulator.TakeSnapshot(inputLocal, dt,
                            SettingsForm.Instance.StoreLastFrames);
                    }
                }

                // render TimelapseScene
                if (needToSimulateTimelapseSceneLocal)
                {
                    GraphicsControl.Dispatcher.Invoke(_dynamicGameSimulator.RenderTimelapseScene);
                }

                // call draw delegate
                if (!GraphicsControl.IsRendering)
                {
                    GraphicsControl.Dispatcher.BeginInvoke(DispatcherPriority.Render,
                        (MethodInvoker) GraphicsControl.InvalidateVisual);
                }

                // fire FieldsChanged event
                if (fieldsChangedStopwacth.ElapsedMilliseconds >= 100 || immediateFieldsChanged)
                {
                    fieldsChangedStopwacth.Restart();
                    immediateFieldsChanged = !FireFieldsChangedEvent();
                }

                // wait
                if (SettingsForm.Instance.WaitAfterEachTick)
                {
                    dtStopwatch.Stop();
                    Thread.Sleep(SettingsForm.Instance.WaitAfterEachTickMsec);
                    dtStopwatch.Start();
                }

                gameTickStopwatch.Stop();

                // log
                if (ii++%40 == 0)
                {
                    _logLabel.Owner.BeginInvoke((MethodInvoker) (() => _logLabel.Text =
                        $"GameTick = {1000.0*gameTickStopwatch.ElapsedTicks/Stopwatch.Frequency:F3}; dt = {dt:F3}"));
                }
            }
        }