Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ScriptDebug.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Collections.Specialized;
5 using System.IO;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using System.Xml.Serialization;
10 using SiliconStudio.Paradox;
11 using SiliconStudio.Paradox.Engine.Network;
12 using SiliconStudio.Paradox.Effects;
13 using SiliconStudio.Paradox.Engine;
14 using SiliconStudio.Paradox.EntityModel;
15 using SiliconStudio.Paradox.Configuration;
16 using SiliconStudio.Core.Extensions;
17 using SiliconStudio.Paradox.Games.IO;
18 using SiliconStudio.Paradox.Games.MicroThreading;
19 using SiliconStudio.Paradox.Games.ViewModel;
20 using SiliconStudio.Paradox.Games.Serialization;
21 using SiliconStudio.Paradox.Prefabs;
22 
23 using ScriptTest2;
24 #if NET45
25 using TaskEx = System.Threading.Tasks.Task;
26 #endif
27 
28 namespace ScriptTest
29 {
30  [Framework.Serialization.SerializableExtended]
31  public class EntitiesUpdatePacket
32  {
33  /// <summary>
34  /// Gets or sets the index of the last acknowledged packet.
35  /// This is useful for client-side prediction, i.e. when user input values
36  /// and they should still be displayed until at least one packet round-trip
37  /// has been done. After that time, engine value coming from new update
38  /// packets should be up to date.
39  /// </summary>
40  public int AckIndex { get; set; }
41  public Dictionary<string, byte[]> Data { get; set; }
42  }
43 
44  [Framework.Serialization.SerializableExtended]
45  public class EntitiesChangePacket
46  {
47  /// <summary>
48  /// Gets or sets the index of this update packet.
49  /// </summary>
50  public int Index { get; set; }
51  public string GroupKey { get; set; }
52  public NetworkChange[] Changes { get; set; }
53  }
54 
55  [ParadoxScript]
56  public class ScriptDebug
57  {
58  private static ViewModelContext selectedEntitiesContext;
59  private static AsyncSignal entitiesChangePacketEvent = new AsyncSignal();
60  private static PickingSystem pickingSystem;
61 
62  public class Config
63  {
64  public Config()
65  {
66  DebugManager = false;
67  Port = 11000;
68  }
69 
70  [XmlAttribute("debugmanager")]
71  public bool DebugManager { get; set; }
72 
73  [XmlAttribute("port")]
74  public int Port { get; set; }
75  }
76 
77  public class PendingClient
78  {
81  }
82 
83  public static bool IsDebugManager
84  {
85  get
86  {
87  var configDebug = AppConfig.GetConfiguration<Config>("ScriptDebug");
88  return (configDebug.DebugManager);
89  }
90  }
91 
92  public static void SelectEntity(params Entity[] entities)
93  {
95  }
96 
97  public static void SelectEntity(IEnumerable<Entity> entities)
98  {
99  // Update property editor selection.
100  if (selectedEntitiesContext != null)
101  {
102  selectedEntitiesContext.ViewModelByGuid.Clear();
103  var viewModels = entities
104  .Where(entity => entity != null)
105  .Select(entity => selectedEntitiesContext.GetModelView(entity).Children.First(x => x.PropertyName == "Components"))
106  .ToArray();
107 
108  if (viewModels.Count() > 1)
109  selectedEntitiesContext.Root = ViewModelController.Combine(selectedEntitiesContext, viewModels);
110  else
111  selectedEntitiesContext.Root = viewModels.FirstOrDefault();
112  }
113 
114  // Update picking system (gizmo).
115  // It will also update the remote selection in entity tree view.
116  var entitiesArray = entities.ToArray();
117  if (!ArrayExtensions.ArraysEqual(pickingSystem.SelectedEntities, entitiesArray))
118  pickingSystem.SelectedEntities = entitiesArray;
119 
120  entitiesChangePacketEvent.Set();
121  }
122 
123  public static async Task RunDebug(EngineContext engineContext)
124  {
125  var config = AppConfig.GetConfiguration<Config>("ScriptDebug");
126  var renderingSetup = RenderingSetup.Singleton;
127 
128  engineContext.RenderContext.PrepareEffectPlugins += (effectBuilder, plugins) =>
129  {
130  if (effectBuilder.PickingPassMainPlugin != null)
131  {
132  RenderPassPlugin pickingPlugin;
133  if (engineContext.DataContext.RenderPassPlugins.TryGetValue(effectBuilder.Name == "Gizmo" ? "MouseOverPickingPlugin" : "PickingPlugin", out pickingPlugin))
134  plugins.Add(new PickingShaderPlugin { RenderPassPlugin = (PickingPlugin)pickingPlugin, MainPlugin = effectBuilder.PickingPassMainPlugin });
135  }
136  if (effectBuilder.SupportWireframe)
137  {
138  RenderPassPlugin wireframePlugin;
139  if (engineContext.DataContext.RenderPassPlugins.TryGetValue("WireframePlugin", out wireframePlugin))
140  plugins.Add(new WireframeShaderPlugin { RenderPassPlugin = wireframePlugin, MainTargetPlugin = renderingSetup.MainTargetPlugin });
141  }
142  };
143 
144  pickingSystem = new PickingSystem();
145  pickingSystem.PropertyChanged += pickingSystem_PropertyChanged;
146  engineContext.Scheduler.Add(() => pickingSystem.ProcessGizmoAndPicking(engineContext));
147 
148  var socketContext = new SocketContext();
149  var socketContextAsync = new SocketContext();
150 
151  var currentScheduler = Scheduler.Current;
152 
153  var pendingClient = new PendingClient();
154 
155  socketContext.Connected = (clientSocketContext) =>
156  {
157  lock (pendingClient)
158  {
159  pendingClient.MainSocket = clientSocketContext;
160  if (pendingClient.AsyncSocket != null)
161  currentScheduler.Add(() => ProcessClient(engineContext, pendingClient.MainSocket, pendingClient.AsyncSocket));
162  }
163  };
164  socketContextAsync.Connected = (clientSocketContext) =>
165  {
166  lock (pendingClient)
167  {
168  pendingClient.AsyncSocket = clientSocketContext;
169  if (pendingClient.MainSocket != null)
170  currentScheduler.Add(() => ProcessClient(engineContext, pendingClient.MainSocket, pendingClient.AsyncSocket));
171  }
172  };
173 
174  socketContext.StartServer(config.Port);
175  socketContextAsync.StartServer(config.Port + 1);
176  }
177 
178  static void pickingSystem_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
179  {
180  if (e.PropertyName == "SelectedEntity")
181  {
182  SelectEntity(pickingSystem.SelectedEntities);
183  }
184  }
185 
186  public async static Task ProcessClient(EngineContext engineContext, SocketContext socketContext, SocketContext socketContextAsync)
187  {
188  socketContext.AddPacketHandler<DownloadFileQuery>(
189  async (packet) =>
190  {
191  var stream = await VirtualFileSystem.OpenStreamAsync(packet.Url, VirtualFileMode.Open, VirtualFileAccess.Read);
192  var data = new byte[stream.Length];
193  await stream.ReadAsync(data, 0, data.Length);
194  stream.Close();
195  socketContext.Send(new DownloadFileAnswer { StreamId = packet.StreamId, Data = data });
196  });
197 
198  socketContext.AddPacketHandler<UploadFilePacket>(
199  async (packet) =>
200  {
201  var stream = await VirtualFileSystem.OpenStreamAsync(packet.Url, VirtualFileMode.Create, VirtualFileAccess.Write);
202  await stream.WriteAsync(packet.Data, 0, packet.Data.Length);
203  stream.Close();
204  });
205 
206  var viewModelGlobalContext = new ViewModelGlobalContext();
207 
208  selectedEntitiesContext = new ViewModelContext(viewModelGlobalContext);
209  selectedEntitiesContext.ChildrenPropertyEnumerators.Add(new EntityComponentEnumerator(engineContext));
210  selectedEntitiesContext.ChildrenPropertyEnumerators.Add(new RenderPassPluginEnumerator());
211  selectedEntitiesContext.ChildrenPropertyEnumerators.Add(new ChildrenPropertyInfoEnumerator());
212  //selectedEntitiesContext.ChildrenPropertyEnumerators.Add(new EffectPropertyEnumerator(engineContext));
213 
214  var renderPassHierarchyContext = new ViewModelContext(viewModelGlobalContext);
215  renderPassHierarchyContext.ChildrenPropertyEnumerators.Add(new RenderPassHierarchyEnumerator());
216  renderPassHierarchyContext.Root = new ViewModelNode("Root", engineContext.RenderContext.RootRenderPass).GenerateChildren(renderPassHierarchyContext);
217 
218  var renderPassPluginsContext = new ViewModelContext(viewModelGlobalContext);
219  renderPassPluginsContext.ChildrenPropertyEnumerators.Add(new RenderPassPluginsEnumerator { SelectedRenderPassPluginContext = selectedEntitiesContext });
220  renderPassPluginsContext.Root = new ViewModelNode("Root", new EnumerableViewModelContent<ViewModelReference>(
221  () => engineContext.RenderContext.RenderPassPlugins.Select(x => new ViewModelReference(x, true))));
222 
223 
224  var entityHierarchyEnumerator = new EntityHierarchyEnumerator(engineContext.EntityManager, selectedEntitiesContext);
225  var entityHierarchyContext = new ViewModelContext(viewModelGlobalContext);
226  entityHierarchyContext.ChildrenPropertyEnumerators.Add(entityHierarchyEnumerator);
227  entityHierarchyContext.ChildrenPropertyEnumerators.Add(new ChildrenPropertyInfoEnumerator());
228  entityHierarchyContext.Root = new ViewModelNode("EntityHierarchyRoot", new EnumerableViewModelContent<ViewModelReference>(
229  () => engineContext.EntityManager.Entities
230  .Where(x =>
231  {
232  var transformationComponent = x.Transformation;
233  return (transformationComponent == null || transformationComponent.Parent == null);
234  })
235  .Select(x => new ViewModelReference(x, true))));
236 
237  entityHierarchyEnumerator.SelectedEntities.CollectionChanged += (sender, args) =>
238  {
239  SelectEntity(entityHierarchyEnumerator.SelectedEntities);
240  };
241  //entityHierarchyContext.Root.Children.Add(new ViewModelNode("SelectedItems", EnumerableViewModelContent.FromUnaryLambda<ViewModelReference, ViewModelReference>(new NullViewModelContent(),
242  // (x) => { return new[] { new ViewModelReference(pickingSystem.SelectedEntity) }; })));
243  /*(value) =>
244  {
245  var entityModelView = value != null ? entityHierarchyContext.GetModelView(value.Guid) : null;
246  var entity = entityModelView != null ? (Entity)entityModelView.NodeValue : null;
247  SelectEntity(entity);
248  })));*/
249  entityHierarchyContext.Root.Children.Add(new ViewModelNode("DropEntity", new RootViewModelContent((ExecuteCommand)((viewModel2, parameter) =>
250  {
251  var dropParameters = (DropCommandParameters)parameter;
252 
253  var movedItem = dropParameters.Data is Guid ? entityHierarchyContext.GetModelView((Guid)dropParameters.Data) : null;
254  var newParent = dropParameters.Parent is Guid ? entityHierarchyContext.GetModelView((Guid)dropParameters.Parent) : null;
255 
256  if (newParent == null || movedItem == null)
257  return;
258 
259  var parent = ((Entity)newParent.NodeValue).Transformation;
260  if (dropParameters.TargetIndex > parent.Children.Count)
261  return;
262 
263  var transformationComponent = ((Entity)movedItem.NodeValue).Transformation;
264  transformationComponent.Parent = null;
265  parent.Children.Insert(dropParameters.TargetIndex, transformationComponent);
266  }))));
267 
268  entityHierarchyContext.Root.Children.Add(new ViewModelNode("DropAsset", new RootViewModelContent((ExecuteCommand)(async (viewModel2, parameter) =>
269  {
270  var dropParameters = (DropCommandParameters)parameter;
271 
272  var assetUrl = (string)dropParameters.Data;
273  /*var newParent = entityHierarchyContext.GetModelView((Guid)dropParameters.Parent);
274 
275  if (newParent == null || assetUrl == null)
276  return;
277 
278  var parent = ((Entity)newParent.NodeValue).Transformation;
279  if (dropParameters.ItemIndex > parent.Children.Count)
280  return;*/
281 
282  engineContext.Scheduler.Add(async () =>
283  {
284  // Load prefab entity
285  var loadedEntityPrefab = await engineContext.AssetManager.LoadAsync<Entity>(assetUrl + "#");
286 
287  // Build another entity from prefab
288  var loadedEntity = Prefab.Inherit(loadedEntityPrefab);
289 
290  // Add it to scene
291  engineContext.EntityManager.AddEntity(loadedEntity);
292 
293  if (loadedEntity.ContainsKey(AnimationComponent.Key))
294  {
295  Scheduler.Current.Add(() => AnimScript.AnimateFBXModel(engineContext, loadedEntity));
296  }
297  });
298  }))));
299 
300  var scriptEngineContext = new ViewModelContext(viewModelGlobalContext);
301  scriptEngineContext.ChildrenPropertyEnumerators.Add(new ScriptAssemblyEnumerator(engineContext));
302  scriptEngineContext.ChildrenPropertyEnumerators.Add(new ChildrenPropertyInfoEnumerator());
303  scriptEngineContext.Root = new ViewModelNode(new EnumerableViewModelContent<ViewModelReference>(
304  () => engineContext.ScriptManager.ScriptAssemblies.Select(x => new ViewModelReference(x, true))));
305  scriptEngineContext.Root.Children.Add(new ViewModelNode("RunScript", new RootViewModelContent((ExecuteCommand)(async (viewModel2, parameter) =>
306  {
307  var scriptName = (string)parameter;
308  var matchingScript = engineContext.ScriptManager.Scripts.Where(x => x.TypeName + "." + x.MethodName == scriptName);
309  if (matchingScript.Any())
310  {
311  var scriptEntry = matchingScript.Single();
312  var microThread = engineContext.ScriptManager.RunScript(scriptEntry, null);
313  }
314  }))));
315 
316  var runningScriptsContext = new ViewModelContext(viewModelGlobalContext);
317  runningScriptsContext.ChildrenPropertyEnumerators.Add(new MicroThreadEnumerator(selectedEntitiesContext));
318  runningScriptsContext.ChildrenPropertyEnumerators.Add(new ChildrenPropertyInfoEnumerator());
319  runningScriptsContext.Root = new ViewModelNode("MicroThreads", new EnumerableViewModelContent<ViewModelReference>(
320  () => engineContext.Scheduler.MicroThreads.Select(x => new ViewModelReference(x, true))
321  ));
322 
323  var effectsContext = new ViewModelContext(viewModelGlobalContext);
324  effectsContext.ChildrenPropertyEnumerators.Add(new EffectEnumerator(selectedEntitiesContext));
325  effectsContext.ChildrenPropertyEnumerators.Add(new ChildrenPropertyInfoEnumerator());
326  effectsContext.Root = new ViewModelNode("Effects", new EnumerableViewModelContent<ViewModelReference>(
327  () => engineContext.RenderContext.Effects.Select(x => new ViewModelReference(x, true))
328  ));
329  //effectsContext.Root.Children.Add(new ViewModelNode("PluginDefinitions", new RootViewModelContent()));
330 
331  var assetBrowserContext = new ViewModelContext(viewModelGlobalContext);
332  assetBrowserContext.ChildrenPropertyEnumerators.Add(new AssetBrowserEnumerator(engineContext));
333  assetBrowserContext.ChildrenPropertyEnumerators.Add(new ChildrenPropertyInfoEnumerator());
334  assetBrowserContext.Root = new ViewModelNode("Root", "Root").GenerateChildren(assetBrowserContext);
335 
336  var editorContext = new ViewModelContext(viewModelGlobalContext);
337  editorContext.Root = new ViewModelNode("Root");
338  editorContext.Root.Children.Add(new ViewModelNode("SwitchSelectionMode", new CommandViewModelContent((sender, parameters) => { pickingSystem.ActiveGizmoActionMode = PickingSystem.GizmoAction.None; })));
339  editorContext.Root.Children.Add(new ViewModelNode("SwitchTranslationMode", new CommandViewModelContent((sender, parameters) => { pickingSystem.ActiveGizmoActionMode = PickingSystem.GizmoAction.Translation; })));
340  editorContext.Root.Children.Add(new ViewModelNode("SwitchRotationMode", new CommandViewModelContent((sender, parameters) => { pickingSystem.ActiveGizmoActionMode = PickingSystem.GizmoAction.Rotation; })));
341 
342  var contexts = new Dictionary<string, Tuple<ViewModelContext, ViewModelState>>();
343  contexts.Add("Editor", Tuple.Create(editorContext, new ViewModelState()));
344  contexts.Add("RenderPassPlugins", Tuple.Create(renderPassPluginsContext, new ViewModelState()));
345  contexts.Add("RenderPasses", Tuple.Create(renderPassHierarchyContext, new ViewModelState()));
346  contexts.Add("SelectedEntities", Tuple.Create(selectedEntitiesContext, new ViewModelState()));
347  contexts.Add("EntityHierarchy", Tuple.Create(entityHierarchyContext, new ViewModelState()));
348  contexts.Add("ScriptEngine", Tuple.Create(scriptEngineContext, new ViewModelState()));
349  contexts.Add("MicroThreads", Tuple.Create(runningScriptsContext, new ViewModelState()));
350  contexts.Add("AssetBrowser", Tuple.Create(assetBrowserContext, new ViewModelState()));
351  contexts.Add("Effects", Tuple.Create(effectsContext, new ViewModelState()));
352 
353  int lastAckPacket = 0;
354 
355  var entitiesChangePackets = new ConcurrentQueue<EntitiesChangePacket>();
356  socketContext.AddPacketHandler<EntitiesChangePacket>(
357  (packet) =>
358  {
359  entitiesChangePackets.Enqueue(packet);
360  entitiesChangePacketEvent.Set();
361  });
362 
363  Action asyncThreadStart = () =>
364  {
365  while (true)
366  {
367  Thread.Sleep(100);
368  foreach (var context in contexts)
369  {
370  // Process async data
371  Guid[] path = null;
372  object value = null;
373  lock (context.Value.Item1)
374  {
375  var pendingNode = context.Value.Item1.GetNextPendingAsyncNode();
376  if (pendingNode != null)
377  {
378  value = pendingNode.Value;
379  path = ViewModelController.BuildPath(pendingNode);
380  }
381  }
382  if (path != null)
383  {
384  // Temporary encoding through our serializer (until our serializer are used for packets)
385  var memoryStream = new MemoryStream();
386  var writer = new BinarySerializationWriter(memoryStream);
387  writer.SerializeExtended(null, value, ArchiveMode.Serialize);
388 
389  var change = new NetworkChange { Path = path.ToArray(), Type = NetworkChangeType.ValueUpdateAsync, Value = memoryStream.ToArray() };
390  var packet = new EntitiesChangePacket { GroupKey = context.Key, Changes = new NetworkChange[] { change } };
391  socketContextAsync.Send(packet);
392  break;
393  }
394  }
395  }
396  };
397 
398  new Thread(new ThreadStart(asyncThreadStart)).Start();
399 
400  // TODO: Move some of this code directly inside ViewModelContext/Controller classes
401  while (true)
402  {
403  await TaskEx.WhenAny(TaskEx.Delay(250), entitiesChangePacketEvent.WaitAsync());
404 
405  EntitiesChangePacket packet;
406  while (entitiesChangePackets.TryDequeue(out packet))
407  {
408  ViewModelController.NetworkApplyChanges(contexts[packet.GroupKey].Item1, packet.Changes);
409  lastAckPacket = packet.Index;
410  }
411 
412  // Wait a single frame so that network updates get applied properly by all rendering systems for next update
413  await Scheduler.Current.NextFrame();
414 
415  // If entity disappeared, try to replace it with new one (happen during file reload)
416  // It's little bit cumbersome to test, need some simplification of this specific entity view model root.
417  if (selectedEntitiesContext.Root != null
418  && selectedEntitiesContext.Root.Parent != null
419  && selectedEntitiesContext.Root.Parent.NodeValue is Entity)
420  {
421  var entity = (Entity)selectedEntitiesContext.Root.Parent.NodeValue;
422  if (!engineContext.EntityManager.Entities.Contains(entity))
423  {
424  entity = engineContext.EntityManager.Entities.FirstOrDefault(x => x.Guid == entity.Guid);
425  if (entity != null)
426  {
427  selectedEntitiesContext.ViewModelByGuid.Clear();
428  selectedEntitiesContext.Root = selectedEntitiesContext.GetModelView(entity).Children.First(x => x.PropertyName == "Components");
429  }
430  }
431  }
432 
433  var data = new Dictionary<string, byte[]>();
434  foreach (var context in contexts)
435  {
436  lock (context.Value.Item1)
437  {
438  if (context.Value.Item1.Root != null)
439  context.Value.Item1.AddModelView(context.Value.Item1.Root);
440  ViewModelController.UpdateReferences(context.Value.Item1, true);
441  data[context.Key] = ViewModelController.NetworkSerialize(context.Value.Item1, context.Value.Item2);
442  }
443  }
444 
445  viewModelGlobalContext.UpdateObjects(contexts.Select(x => x.Value.Item1));
446 
447  //Console.WriteLine("DataSize: {0}", data.Sum(x => x.Value.Length));
448  await Task.Factory.StartNew(() => socketContext.Send(new EntitiesUpdatePacket { AckIndex = lastAckPacket, Data = data }));
449  }
450  }
451  }
452 }
static void SelectEntity(params Entity[] entities)
Definition: ScriptDebug.cs:92
Entity[] SelectedEntities
Gets or sets currently selected entity.
Game entity. It usually aggregates multiple EntityComponent
Definition: Entity.cs:28
static async Task AnimateFBXModel(EngineContext engineContext, Entity characterEntity, float endAnimTime=0.0f, float loopTime=0.0f)
Definition: AnimScript.cs:69
static bool IsDebugManager
Definition: ScriptDebug.cs:84
static async Task ProcessClient(EngineContext engineContext, SocketContext socketContext, SocketContext socketContextAsync)
Definition: ScriptDebug.cs:186
System.Threading.Tasks.Task Task
Add animation capabilities to an Entity. It will usually apply to ModelComponent.ModelViewHierarchy ...
static readonly RenderingSetup Singleton
int AckIndex
Gets or sets the index of the last acknowledged packet. This is useful for client-side prediction...
Definition: ScriptDebug.cs:40
int Index
Gets or sets the index of this update packet.
Definition: ScriptDebug.cs:50
static async Task RunDebug(EngineContext engineContext)
Definition: ScriptDebug.cs:123
Dictionary< string, byte[]> Data
Definition: ScriptDebug.cs:41
static PropertyKey< AnimationComponent > Key
async Task ProcessGizmoAndPicking(EngineContext engineContext)
static void SelectEntity(IEnumerable< Entity > entities)
Definition: ScriptDebug.cs:97