glav avatar glav committed 668f018

Basic processing with debugging support

Comments (0)

Files changed (8)

EcoDev.Core/Common/IWorld.cs

 	// world to be available
 	public interface IWorld
 	{
+		void WriteDebugInformation(string source, string message);
 	}
 }

EcoDev/EcoDev.Engine.csproj

     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="WorldEngine\ActionResponse.cs" />
     <Compile Include="WorldEngine\ActionResponseFactory.cs" />
+    <Compile Include="WorldEngine\ActionResultContext.cs" />
+    <Compile Include="WorldEngine\AsyncActionExecutionEngine.cs" />
     <Compile Include="WorldEngine\EcoWorld.cs" />
     <Compile Include="WorldEngine\MovementActionResponse.cs" />
   </ItemGroup>

EcoDev/Entities/LivingEntityWithQualities.cs

 		{
 			Qualities = new LivingEntityQualities();
 			PositionInMap = new MapPosition();
+			IsDead = false;
 		}
 		public LivingEntity Entity { get; set; }
 		public LivingEntityQualities Qualities { get; set; }
 		public MapPosition PositionInMap { get; set; }
 		public WorldAxis ForwardFacingAxis { get; set; }
+		public bool IsDead { get; set; }
 	}
 }

EcoDev/WorldEngine/ActionResultContext.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using EcoDev.Core.Common.Actions;
+
+namespace EcoDev.Engine.WorldEngine
+{
+	public class ActionResultContext
+	{
+		public ActionResultContext()
+		{
+
+		}
+		public ActionResultContext(ActionResult actionResult)
+		{
+			ActionResult.DecidedAction = actionResult.DecidedAction;
+			ActionResult.DirectionToMove = actionResult.DirectionToMove;
+		}
+		public ActionResult ActionResult { get; set; }
+		public Exception ErrorException { get; set; }
+	}
+}

EcoDev/WorldEngine/AsyncActionExecutionEngine.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using EcoDev.Engine.Entities;
+using EcoDev.Core.Common.Actions;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Diagnostics;
+
+namespace EcoDev.Engine.WorldEngine
+{
+	public class AsyncActionExecutionEngine
+	{
+		LivingEntityWithQualities _entity;
+		ActionContext _context;
+		CancellationTokenSource _tokenSource = new CancellationTokenSource();
+#if DEBUG
+		public const int MAX_MILLISECONDS_ALLOWED_FOR_ENTITY_DECISION = 2000;
+#else
+		public const int MAX_MILLISECONDS_ALLOWED_FOR_ENTITY_DECISION = 500;
+#endif
+
+		public AsyncActionExecutionEngine(LivingEntityWithQualities entity, ActionContext context)
+		{
+			_entity = entity;
+			_context = context;
+		}
+
+		public ActionResultContext DetermineEntityAction()
+		{
+			var decisionTask = new Task<ActionResultContext>(() =>
+			{
+				ActionResultContext result;
+				try
+				{
+					result = new ActionResultContext(_entity.Entity.DecideActionToPerform(_context));
+				}
+				catch (Exception ex)
+				{
+					result = new ActionResultContext() { ErrorException = ex };
+					_entity.IsDead = true;
+				}
+				return result;
+			}, _tokenSource.Token);
+
+			decisionTask.Start();
+			decisionTask.Wait(MAX_MILLISECONDS_ALLOWED_FOR_ENTITY_DECISION);
+
+			// If the decision has not been reached after the time period, signal the task to close
+			if (!decisionTask.IsCompleted && !decisionTask.IsCanceled && !decisionTask.IsFaulted)
+			{
+				_tokenSource.Cancel();
+				System.Threading.Thread.Sleep(200);
+				if (decisionTask.IsCompleted || decisionTask.IsCanceled || decisionTask.IsFaulted)
+				{
+					decisionTask.Dispose();
+				}
+				return new ActionResultContext(new MovementAction());
+			}
+			else
+			{
+				return decisionTask.Result;
+			}
+
+			return new ActionResultContext(new MovementAction());
+
+		}
+
+	}
+}

EcoDev/WorldEngine/EcoWorld.cs

 
 namespace EcoDev.Engine.WorldEngine
 {
+	public class DebugInfoEventArgs : EventArgs
+	{
+		public DebugInfoEventArgs(string debugInformation)
+		{
+			DebugInformation = debugInformation;
+		}
+
+		public string DebugInformation { get; set; }
+	}
+
 	public class EcoWorld : IWorld
 	{
 		Map _worldMap;
 		List<LivingEntityWithQualities> _inhabitants = new List<LivingEntityWithQualities>();
 		string _worldName;
 		Task _worldTask = null;
+		const int MIN_MILLISECONDS_TO_CYCLE_THROUGH_PLAYER_ACTIONS = 5000;
+
+		public event EventHandler<DebugInfoEventArgs> DebugInformation;
 
 		public EcoWorld(string worldName, Map worldMap, LivingEntityWithQualities[] inhabitants)
 		{
 		}
 
 		public Map WorldMap { get { return _worldMap; } }
-		public IEnumerable<LivingEntityWithQualities> Inhabitants{get { return _inhabitants; }}
+		public IEnumerable<LivingEntityWithQualities> Inhabitants { get { return _inhabitants; } }
 
 		public void AddPlayer(LivingEntityWithQualities player)
 		{
 			_inhabitants.Add(player);
 		}
 
+		private void FireDebugInfoEvent(string debugInfo)
+		{
+			if (DebugInformation != null)
+			{
+				string fullInfo = string.Format("[{0} - {1}] {2}{3}", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), debugInfo, Environment.NewLine);
+				DebugInformation(this, new DebugInfoEventArgs(fullInfo));
+			}
+		}
+
 		public void StartWorld()
 		{
+			FireDebugInfoEvent(string.Format("{0}************{0}World [{1}]{0}************{0}Starting World: '{1}' processing loop", Environment.NewLine, _worldName));
+
 			_worldTask = Task.Factory.StartNew(new Action(WorldProcessingTask), _tokenSource.Token);
 		}
 
 
 		internal void WorldProcessingTask()
 		{
-			Trace.WriteLine(string.Format("Starting World Processing: [{0}]", _worldName));
+			FireDebugInfoEvent(string.Format("Starting World Processing: [{0}]", _worldName));
 
 			while (!_tokenSource.IsCancellationRequested)
 			{
 				//TODO: Go through all players. If world == null, then place players at start and allow a movement
 
 				//TODO: then cycle through players/inhabitants and process movements
+				ProcessPlayers();
+
+				CleanupWorld();
 			}
 
 		}
 
+		private void CleanupWorld()
+		{
+			//TODO: Remove any dead players from world.
+		}
+
+		internal void ProcessPlayers()
+		{
+			var timer = new Stopwatch();
+			timer.Start();
+			for (int loop = 0; loop < _inhabitants.Count; loop++)
+			{
+				var entity = _inhabitants[loop];
+				PerformEntityAction(entity);
+			}
+
+			while (timer.ElapsedMilliseconds < MIN_MILLISECONDS_TO_CYCLE_THROUGH_PLAYER_ACTIONS) { }
+		}
+
 		internal void AddInhabitantsToMapIfRequired()
 		{
+			FireDebugInfoEvent("Adding inhabitants to map");
+			int addedCount = 0;
+
 			if (_inhabitants != null && _inhabitants.Count > 0)
 			{
 				for (var cnt = 0; cnt < _inhabitants.Count; cnt++)
 						entity.PositionInMap = entrance;
 						entity.ForwardFacingAxis = entrance.DetermineForwardFacingPositionBasedOnThisPosition(_worldMap.WidthInUnits, _worldMap.HeightInUnits, _worldMap.DepthInUnits);
 						PerformEntityAction(entity);
+						addedCount++;
 					}
 				}
 			}
+			FireDebugInfoEvent(string.Format("Added {0} new inhabitants to world", addedCount));
 		}
 
 
 		private void PerformEntityAction(LivingEntityWithQualities entity)
 		{
+			if (entity.IsDead)
+			{
+				return;
+			}
+
+			FireDebugInfoEvent(string.Format("Performing entity action on Entity:{0}", entity.Entity.Name));
 			var positionContext = ConstructPositionContextForEntity(entity);
 			ActionContext context = new ActionContext(positionContext);
-			var actionResult = entity.Entity.DecideActionToPerform(context);
-			ActOnEntityActionResult(entity,actionResult);
+
+			var asyncEngine = new AsyncActionExecutionEngine(entity, context);
+			var result = asyncEngine.DetermineEntityAction();
+
+			if (result.ErrorException == null)
+			{
+				ActOnEntityActionResult(entity, result.ActionResult);
+			}
+			else
+			{
+				//TODO: Mark entity as invalid and remove in garbage collection phase
+				FireDebugInfoEvent(string.Format("Player {0} threw exception. Marking as invalid and removing from world.", entity.Entity.Name));
+			}
 		}
 
 		private void ActOnEntityActionResult(LivingEntityWithQualities entity, ActionResult actionResult)
 		{
+			FireDebugInfoEvent(string.Format("Acting on Entity:{0} Result, ActionResult:{1}", entity.Entity.Name, actionResult.DecidedAction));
 			var responseActionHandler = ActionResponseFactory.CreateActionResponseHandler(actionResult, entity);
 			try
 			{
 			{
 				case WorldAxis.PositiveX:
 					fwdFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition + 1, entity.PositionInMap.yPosition, entity.PositionInMap.zPosition));
-					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition - 1,entity.PositionInMap.yPosition,entity.PositionInMap.zPosition));
-					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition,entity.PositionInMap.yPosition+1,entity.PositionInMap.zPosition));
-					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition,entity.PositionInMap.yPosition-1,entity.PositionInMap.zPosition));
+					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition - 1, entity.PositionInMap.yPosition, entity.PositionInMap.zPosition));
+					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition + 1, entity.PositionInMap.zPosition));
+					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition - 1, entity.PositionInMap.zPosition));
 					break;
 				case WorldAxis.PositiveY:
 					fwdFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition + 1, entity.PositionInMap.zPosition));
-					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition,entity.PositionInMap.yPosition-1,entity.PositionInMap.zPosition));
-					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition-1,entity.PositionInMap.yPosition+1,entity.PositionInMap.zPosition));
-					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition + 1,entity.PositionInMap.yPosition,entity.PositionInMap.zPosition));
+					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition - 1, entity.PositionInMap.zPosition));
+					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition - 1, entity.PositionInMap.yPosition + 1, entity.PositionInMap.zPosition));
+					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition + 1, entity.PositionInMap.yPosition, entity.PositionInMap.zPosition));
 					break;
 				case WorldAxis.PositiveZ:
 					break;
 				case WorldAxis.NegativeX:
 					fwdFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition - 1, entity.PositionInMap.yPosition, entity.PositionInMap.zPosition));
-					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition + 1,entity.PositionInMap.yPosition,entity.PositionInMap.zPosition));
-					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition,entity.PositionInMap.yPosition-1,entity.PositionInMap.zPosition));
-					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition,entity.PositionInMap.yPosition+1,entity.PositionInMap.zPosition));
+					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition + 1, entity.PositionInMap.yPosition, entity.PositionInMap.zPosition));
+					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition - 1, entity.PositionInMap.zPosition));
+					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition + 1, entity.PositionInMap.zPosition));
 					break;
 				case WorldAxis.NegativeY:
 					fwdFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition - 1, entity.PositionInMap.zPosition));
-					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition,entity.PositionInMap.yPosition+1,entity.PositionInMap.zPosition));
-					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition+1,entity.PositionInMap.yPosition+1,entity.PositionInMap.zPosition));
-					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition - 1,entity.PositionInMap.yPosition,entity.PositionInMap.zPosition));
+					rearFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition, entity.PositionInMap.yPosition + 1, entity.PositionInMap.zPosition));
+					leftFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition + 1, entity.PositionInMap.yPosition + 1, entity.PositionInMap.zPosition));
+					rightFacingBlocks.Add(_worldMap.Get(entity.PositionInMap.xPosition - 1, entity.PositionInMap.yPosition, entity.PositionInMap.zPosition));
 					break;
 				case WorldAxis.NegativeZ:
 					break;
 			}
 
-			var posContext = new PositionContext(currentPosition,fwdFacingBlocks.ToArray(),rearFacingBlocks.ToArray(),leftFacingBlocks.ToArray(),rightFacingBlocks.ToArray());
+			var posContext = new PositionContext(currentPosition, fwdFacingBlocks.ToArray(), rearFacingBlocks.ToArray(), leftFacingBlocks.ToArray(), rightFacingBlocks.ToArray());
 			return posContext;
 		}
 
 		internal MapPosition FindAnEntrance()
-		{	
+		{
 			if (_worldMap.Entrances.Count() == 0)
 			{
 				throw new MapInvalidException("No entrances available in world or world not initialised");
 				return allEntrances[rnd.Next(1, allEntrances.Count)];
 			}
 		}
+
+		public void WriteDebugInformation(string source, string message)
+		{
+			string realMsg = string.Format("Source: [{0}]{1}  ->: {2}", source, Environment.NewLine, message);
+			FireDebugInfoEvent(realMsg);
+		}
 	}
 }

TestEcoWorldHost/Program.cs

 using EcoDev.Core.Common.Actions;
 using EcoDev.Engine.MapEngine;
 using EcoDev.Core.Common.BuildingBlocks;
+using System.IO;
 
 namespace TestEcoWorldHost
 {
 		static void Main(string[] args)
 		{
 			var world = CreateWorld();
+			world.DebugInformation += new EventHandler<DebugInfoEventArgs>(world_DebugInformation);
 			var player = CreatePlayer();
 
 			WriteDebuggingInfo(world);
 			world.DestroyWorld();
 		}
 
+		static void world_DebugInformation(object sender, DebugInfoEventArgs e)
+		{
+			Console.WriteLine(e.DebugInformation);
+			using (var file = File.Open("WorldDebugInfo.log", FileMode.Append))
+			{
+				var data = ASCIIEncoding.ASCII.GetBytes(e.DebugInformation);
+				file.Write(data, 0, data.Length);
+			}
+		}
+
 		private static void WriteDebuggingInfo(EcoWorld world)
 		{
-			System.IO.File.WriteAllText(".\\WorldDebug.txt", world.WorldMap.ToString());
+			System.IO.File.WriteAllText(".\\WorldMapDebug.txt", world.WorldMap.ToString());
 		}
 
 		private static LivingEntityWithQualities CreatePlayer()

TestEcoWorldHost/TestPlayer.cs

 		}
 		public override ActionResult DecideActionToPerform(EcoDev.Core.Common.Actions.ActionContext actionContext)
 		{
-			var action = new MovementAction();
+			try
+			{
+				var action = new MovementAction();
 
-			// move forward if we can
-			if (actionContext.Position.ForwardFacingPositions.Length > 0)
+				// move forward if we can
+				if (actionContext.Position.ForwardFacingPositions.Length > 0)
+				{
+					if (actionContext.Position.ForwardFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
+					{
+						return action;
+					}
+				}
+				if (actionContext.Position.LeftFacingPositions.Length > 0)
+				{
+					if (actionContext.Position.LeftFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
+					{
+						action.DirectionToMove = MovementDirection.Left;
+						return action;
+					}
+				}
+				if (actionContext.Position.RightFacingPositions.Length > 0)
+				{
+					if (actionContext.Position.RightFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
+					{
+						action.DirectionToMove = MovementDirection.Right;
+						return action;
+					}
+				}
+				if (actionContext.Position.RearFacingPositions.Length > 0)
+				{
+					if (actionContext.Position.RearFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
+					{
+						action.DirectionToMove = MovementDirection.Back;
+						return action;
+					}
+				}
+
+				return action;
+			}
+			catch (Exception ex)
 			{
-				if (actionContext.Position.ForwardFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
-				{
-					return action;
-				}
+				World.WriteDebugInformation("Player: "+ Name, string.Format("Player Generated exception: {0}",ex.Message));
+				throw ex;
 			}
-			if (actionContext.Position.LeftFacingPositions.Length > 0)
-			{
-				if (actionContext.Position.LeftFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
-				{
-					action.DirectionToMove = MovementDirection.Left;
-					return action;
-				}
-			}
-			if (actionContext.Position.RightFacingPositions.Length > 0)
-			{
-				if (actionContext.Position.RightFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
-				{
-					action.DirectionToMove = MovementDirection.Right;
-					return action;
-				}
-			}
-			if (actionContext.Position.RearFacingPositions.Length > 0)
-			{
-				if (actionContext.Position.RearFacingPositions[0].Accessibility == MapBlockAccessibility.CannotGainEntryOrExit)
-				{
-					action.DirectionToMove = MovementDirection.Back;
-					return action;
-				}
-			}
-
-			return action;
 		}
 
 	}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.