Merge remote-tracking branch 'origin/master' into 1.6
# Conflicts: # Source/Tools/Flax.Build/Build/Builder.Projects.cs # Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@
|
|||||||
obj/
|
obj/
|
||||||
Cache/
|
Cache/
|
||||||
Binaries/
|
Binaries/
|
||||||
|
Output/
|
||||||
Logs/
|
Logs/
|
||||||
Source/*.csproj
|
Source/*.csproj
|
||||||
/Package_*/
|
/Package_*/
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace FlaxEditor.Content
|
|||||||
/// Json assets proxy.
|
/// Json assets proxy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
||||||
[ContentContextMenu("New/Json Asset")]
|
|
||||||
public abstract class JsonAssetProxy : JsonAssetBaseProxy
|
public abstract class JsonAssetProxy : JsonAssetBaseProxy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -127,6 +126,7 @@ namespace FlaxEditor.Content
|
|||||||
/// Generic Json assets proxy (supports all json assets that don't have dedicated proxy).
|
/// Generic Json assets proxy (supports all json assets that don't have dedicated proxy).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
||||||
|
[ContentContextMenu("New/Json Asset")]
|
||||||
public class GenericJsonAssetProxy : JsonAssetProxy
|
public class GenericJsonAssetProxy : JsonAssetProxy
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -327,8 +327,8 @@ bool CookAssetsStep::ProcessDefaultAsset(AssetCookData& options)
|
|||||||
{
|
{
|
||||||
// Use compact json
|
// Use compact json
|
||||||
rapidjson_flax::StringBuffer buffer;
|
rapidjson_flax::StringBuffer buffer;
|
||||||
rapidjson_flax::Writer<rapidjson_flax::StringBuffer> writer(buffer);
|
CompactJsonWriter writerObj(buffer);
|
||||||
asJsonAsset->Document.Accept(writer);
|
asJsonAsset->Save(writerObj);
|
||||||
|
|
||||||
// Store json data in the first chunk
|
// Store json data in the first chunk
|
||||||
auto chunk = New<FlaxChunk>();
|
auto chunk = New<FlaxChunk>();
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
// Draw tangent points
|
// Draw tangent points
|
||||||
if (tangentIn != pos)
|
if (tangentIn != pos)
|
||||||
{
|
{
|
||||||
DebugDraw.DrawLine(pos, tangentIn, Color.White.AlphaMultiplied(0.6f), 0, false);
|
DebugDraw.DrawLine(pos, tangentIn, Color.Blue.AlphaMultiplied(0.6f), 0, false);
|
||||||
DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, 4.0f), Color.White, 0, false);
|
DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, 4.0f), Color.Blue, 0, false);
|
||||||
}
|
}
|
||||||
if (tangentIn != pos)
|
if (tangentOut != pos)
|
||||||
{
|
{
|
||||||
DebugDraw.DrawLine(pos, tangentOut, Color.White.AlphaMultiplied(0.6f), 0, false);
|
DebugDraw.DrawLine(pos, tangentOut, Color.Red.AlphaMultiplied(0.6f), 0, false);
|
||||||
DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.White, 0, false);
|
DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.Red, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -250,6 +250,9 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
set => Values[1] = value;
|
set => Values[1] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => Context;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
@@ -288,6 +291,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
private bool _isMouseDown;
|
private bool _isMouseDown;
|
||||||
private Rectangle _textRect;
|
private Rectangle _textRect;
|
||||||
private Rectangle _dragAreaRect;
|
private Rectangle _dragAreaRect;
|
||||||
|
private bool _cursorChanged = false;
|
||||||
|
private bool _textRectHovered = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the first state node identifier for the state machine pointed by the entry node.
|
/// Gets or sets the first state node identifier for the state machine pointed by the entry node.
|
||||||
@@ -336,7 +341,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var width = Mathf.Max(100, titleSize.X + 50);
|
var width = Mathf.Max(100, titleSize.X + 50);
|
||||||
Resize(width, 0);
|
Resize(width, 0);
|
||||||
titleSize.X += 8.0f;
|
titleSize.X += 8.0f;
|
||||||
_dragAreaRect = new Rectangle((Size - titleSize) * 0.5f, titleSize);
|
var padding = new Float2(8, 8);
|
||||||
|
_dragAreaRect = new Rectangle(padding, Size - padding * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -345,10 +351,19 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
|
|
||||||
// Paint Background
|
// Paint Background
|
||||||
BackgroundColor = _isSelected ? Color.OrangeRed : style.BackgroundNormal;
|
if (_isSelected)
|
||||||
|
Render2D.DrawRectangle(_textRect, Color.Orange);
|
||||||
|
|
||||||
|
BackgroundColor = style.BackgroundNormal;
|
||||||
|
var dragAreaColor = BackgroundColor / 2.0f;
|
||||||
|
|
||||||
if (IsMouseOver)
|
if (IsMouseOver)
|
||||||
BackgroundColor *= 1.2f;
|
BackgroundColor *= 1.2f;
|
||||||
|
if (_textRectHovered)
|
||||||
|
BackgroundColor *= 1.2f;
|
||||||
|
|
||||||
Render2D.FillRectangle(_textRect, BackgroundColor);
|
Render2D.FillRectangle(_textRect, BackgroundColor);
|
||||||
|
Render2D.FillRectangle(_dragAreaRect, dragAreaColor);
|
||||||
|
|
||||||
// Push clipping mask
|
// Push clipping mask
|
||||||
if (ClipChildren)
|
if (ClipChildren)
|
||||||
@@ -382,6 +397,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
_isMouseDown = true;
|
_isMouseDown = true;
|
||||||
Cursor = CursorType.Hand;
|
Cursor = CursorType.Hand;
|
||||||
|
_cursorChanged = true;
|
||||||
Focus();
|
Focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -399,6 +415,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
_isMouseDown = false;
|
_isMouseDown = false;
|
||||||
Cursor = CursorType.Default;
|
Cursor = CursorType.Default;
|
||||||
|
_cursorChanged = false;
|
||||||
Surface.ConnectingEnd(this);
|
Surface.ConnectingEnd(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,6 +429,25 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public override void OnMouseMove(Float2 location)
|
public override void OnMouseMove(Float2 location)
|
||||||
{
|
{
|
||||||
Surface.ConnectingOver(this);
|
Surface.ConnectingOver(this);
|
||||||
|
if (_dragAreaRect.Contains(location) && !_cursorChanged && !Input.GetMouseButton(MouseButton.Left))
|
||||||
|
{
|
||||||
|
Cursor = CursorType.SizeAll;
|
||||||
|
_cursorChanged = true;
|
||||||
|
}
|
||||||
|
else if (!_dragAreaRect.Contains(location) && _cursorChanged)
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
_cursorChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_textRect.Contains(location) && !_dragAreaRect.Contains(location) && !_textRectHovered)
|
||||||
|
{
|
||||||
|
_textRectHovered = true;
|
||||||
|
}
|
||||||
|
else if (_textRectHovered && (!_textRect.Contains(location) || _dragAreaRect.Contains(location)))
|
||||||
|
{
|
||||||
|
_textRectHovered = false;
|
||||||
|
}
|
||||||
base.OnMouseMove(location);
|
base.OnMouseMove(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +456,15 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
base.OnMouseLeave();
|
base.OnMouseLeave();
|
||||||
|
|
||||||
|
if (_cursorChanged)
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
_cursorChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_textRectHovered)
|
||||||
|
_textRectHovered = false;
|
||||||
|
|
||||||
if (_isMouseDown)
|
if (_isMouseDown)
|
||||||
{
|
{
|
||||||
_isMouseDown = false;
|
_isMouseDown = false;
|
||||||
@@ -606,6 +651,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
private Rectangle _textRect;
|
private Rectangle _textRect;
|
||||||
private Rectangle _dragAreaRect;
|
private Rectangle _dragAreaRect;
|
||||||
private Rectangle _renameButtonRect;
|
private Rectangle _renameButtonRect;
|
||||||
|
private bool _cursorChanged = false;
|
||||||
|
private bool _textRectHovered = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The transitions list from this state to the others.
|
/// The transitions list from this state to the others.
|
||||||
@@ -711,7 +758,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var width = Mathf.Max(100, titleSize.X + 50);
|
var width = Mathf.Max(100, titleSize.X + 50);
|
||||||
Resize(width, 0);
|
Resize(width, 0);
|
||||||
titleSize.X += 8.0f;
|
titleSize.X += 8.0f;
|
||||||
_dragAreaRect = new Rectangle((Size - titleSize) * 0.5f, titleSize);
|
var padding = new Float2(8, 8);
|
||||||
|
_dragAreaRect = new Rectangle(padding, Size - padding * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -1138,10 +1186,19 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
|
|
||||||
// Paint Background
|
// Paint Background
|
||||||
BackgroundColor = _isSelected ? Color.OrangeRed : style.BackgroundNormal;
|
if (_isSelected)
|
||||||
|
Render2D.DrawRectangle(_textRect, Color.Orange);
|
||||||
|
|
||||||
|
BackgroundColor = style.BackgroundNormal;
|
||||||
|
var dragAreaColor = BackgroundColor / 2.0f;
|
||||||
|
|
||||||
if (IsMouseOver)
|
if (IsMouseOver)
|
||||||
BackgroundColor *= 1.2f;
|
BackgroundColor *= 1.2f;
|
||||||
|
if (_textRectHovered)
|
||||||
|
BackgroundColor *= 1.2f;
|
||||||
|
|
||||||
Render2D.FillRectangle(_textRect, BackgroundColor);
|
Render2D.FillRectangle(_textRect, BackgroundColor);
|
||||||
|
Render2D.FillRectangle(_dragAreaRect, dragAreaColor);
|
||||||
|
|
||||||
// Push clipping mask
|
// Push clipping mask
|
||||||
if (ClipChildren)
|
if (ClipChildren)
|
||||||
@@ -1194,6 +1251,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
_isMouseDown = true;
|
_isMouseDown = true;
|
||||||
Cursor = CursorType.Hand;
|
Cursor = CursorType.Hand;
|
||||||
|
_cursorChanged = true;
|
||||||
Focus();
|
Focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1211,6 +1269,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
_isMouseDown = false;
|
_isMouseDown = false;
|
||||||
Cursor = CursorType.Default;
|
Cursor = CursorType.Default;
|
||||||
|
_cursorChanged = false;
|
||||||
Surface.ConnectingEnd(this);
|
Surface.ConnectingEnd(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1231,6 +1290,26 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public override void OnMouseMove(Float2 location)
|
public override void OnMouseMove(Float2 location)
|
||||||
{
|
{
|
||||||
Surface.ConnectingOver(this);
|
Surface.ConnectingOver(this);
|
||||||
|
if (_dragAreaRect.Contains(location) && !_cursorChanged && !_renameButtonRect.Contains(location) && !_closeButtonRect.Contains(location) && !Input.GetMouseButton(MouseButton.Left))
|
||||||
|
{
|
||||||
|
Cursor = CursorType.SizeAll;
|
||||||
|
_cursorChanged = true;
|
||||||
|
}
|
||||||
|
else if ((!_dragAreaRect.Contains(location) || _renameButtonRect.Contains(location) || _closeButtonRect.Contains(location)) && _cursorChanged)
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
_cursorChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_textRect.Contains(location) && !_dragAreaRect.Contains(location) && !_textRectHovered)
|
||||||
|
{
|
||||||
|
_textRectHovered = true;
|
||||||
|
}
|
||||||
|
else if (_textRectHovered && (!_textRect.Contains(location) || _dragAreaRect.Contains(location)))
|
||||||
|
{
|
||||||
|
_textRectHovered = false;
|
||||||
|
}
|
||||||
|
|
||||||
base.OnMouseMove(location);
|
base.OnMouseMove(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1239,6 +1318,15 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
base.OnMouseLeave();
|
base.OnMouseLeave();
|
||||||
|
|
||||||
|
if (_cursorChanged)
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
_cursorChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_textRectHovered)
|
||||||
|
_textRectHovered = false;
|
||||||
|
|
||||||
if (_isMouseDown)
|
if (_isMouseDown)
|
||||||
{
|
{
|
||||||
_isMouseDown = false;
|
_isMouseDown = false;
|
||||||
@@ -1311,6 +1399,9 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
set => Values[1] = value;
|
set => Values[1] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => Context;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
@@ -1682,6 +1773,9 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
set => RuleGraph = value;
|
set => RuleGraph = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => SourceState.Context;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Title = "Blend with Mask",
|
Title = "Blend with Mask",
|
||||||
Description = "Blend animation poses using skeleton mask",
|
Description = "Blend animation poses using skeleton mask",
|
||||||
Flags = NodeFlags.AnimGraph,
|
Flags = NodeFlags.AnimGraph,
|
||||||
Size = new Float2(180, 100),
|
Size = new Float2(180, 140),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
0.0f,
|
0.0f,
|
||||||
@@ -496,7 +496,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Input(0, "Pose A", true, typeof(void), 1),
|
NodeElementArchetype.Factory.Input(0, "Pose A", true, typeof(void), 1),
|
||||||
NodeElementArchetype.Factory.Input(1, "Pose B", true, typeof(void), 2),
|
NodeElementArchetype.Factory.Input(1, "Pose B", true, typeof(void), 2),
|
||||||
NodeElementArchetype.Factory.Input(2, "Alpha", true, typeof(float), 3, 0),
|
NodeElementArchetype.Factory.Input(2, "Alpha", true, typeof(float), 3, 0),
|
||||||
NodeElementArchetype.Factory.Asset(100, 20, 1, typeof(SkeletonMask)),
|
NodeElementArchetype.Factory.Asset(0, 70, 1, typeof(SkeletonMask)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
byte[] SurfaceData { get; set; }
|
byte[] SurfaceData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the context which owns this surface context (null for root).
|
||||||
|
/// </summary>
|
||||||
|
VisjectSurfaceContext ParentContext { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when Visject Surface context gets created for this surface data source. Can be used to link for some events.
|
/// Called when Visject Surface context gets created for this surface data source. Can be used to link for some events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
|
|
||||||
CaptureConnections(iB, out _inputBefore);
|
CaptureConnections(iB, out _inputBefore);
|
||||||
CaptureConnections(oB, out _outputBefore);
|
CaptureConnections(oB, out _outputBefore);
|
||||||
|
|
||||||
|
#if BUILD_DEBUG
|
||||||
|
// Validate handles
|
||||||
|
if (_context.Get(_surface) != iB.ParentNode.Context)
|
||||||
|
throw new System.Exception("Invalid ContextHandle");
|
||||||
|
if (_input.Get(iB.ParentNode.Context) != iB || _output.Get(oB.ParentNode.Context) != oB)
|
||||||
|
throw new System.Exception("Invalid BoxHandle");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void End()
|
public void End()
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.Json;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface.Undo
|
namespace FlaxEditor.Surface.Undo
|
||||||
{
|
{
|
||||||
@@ -9,7 +11,7 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
/// The helper structure for Surface context handle.
|
/// The helper structure for Surface context handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public struct ContextHandle
|
public struct ContextHandle : IEquatable<ContextHandle>
|
||||||
{
|
{
|
||||||
private readonly string[] _path;
|
private readonly string[] _path;
|
||||||
|
|
||||||
@@ -42,6 +44,31 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ContextHandle"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="child">The child context provider.</param>
|
||||||
|
public ContextHandle(ISurfaceContext child)
|
||||||
|
{
|
||||||
|
if (child == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
VisjectSurfaceContext parent = child.ParentContext;
|
||||||
|
VisjectSurfaceContext context = parent;
|
||||||
|
int count = 1;
|
||||||
|
while (parent != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
parent = parent.Parent;
|
||||||
|
}
|
||||||
|
_path = new string[count];
|
||||||
|
_path[0] = child.SurfaceName;
|
||||||
|
for (int i = 1; i < count; i++)
|
||||||
|
{
|
||||||
|
_path[i] = context.Context.SurfaceName;
|
||||||
|
context = context.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the context.
|
/// Gets the context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -69,5 +96,46 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Equals(ContextHandle other)
|
||||||
|
{
|
||||||
|
return JsonSerializer.ValueEquals(_path, other._path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ContextHandle other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 17;
|
||||||
|
if (_path != null)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
for (var i = 0; i < _path.Length; i++)
|
||||||
|
{
|
||||||
|
var item = _path[i];
|
||||||
|
hash = hash * 23 + (item != null ? item.GetHashCode() : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
if (_path == null)
|
||||||
|
return string.Empty;
|
||||||
|
for (int i = _path.Length - 1; i >= 0; i--)
|
||||||
|
sb.Append(_path[i]).Append('/');
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using FlaxEditor.Surface.Undo;
|
||||||
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
{
|
{
|
||||||
@@ -9,7 +11,7 @@ namespace FlaxEditor.Surface
|
|||||||
{
|
{
|
||||||
private VisjectSurfaceContext _root;
|
private VisjectSurfaceContext _root;
|
||||||
private VisjectSurfaceContext _context;
|
private VisjectSurfaceContext _context;
|
||||||
private readonly Dictionary<ISurfaceContext, VisjectSurfaceContext> _contextCache = new Dictionary<ISurfaceContext, VisjectSurfaceContext>();
|
private readonly Dictionary<ContextHandle, VisjectSurfaceContext> _contextCache = new Dictionary<ContextHandle, VisjectSurfaceContext>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The surface context stack.
|
/// The surface context stack.
|
||||||
@@ -54,11 +56,12 @@ namespace FlaxEditor.Surface
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Get or create context
|
// Get or create context
|
||||||
if (!_contextCache.TryGetValue(context, out VisjectSurfaceContext surfaceContext))
|
var contextHandle = new ContextHandle(context);
|
||||||
|
if (!_contextCache.TryGetValue(contextHandle, out VisjectSurfaceContext surfaceContext))
|
||||||
{
|
{
|
||||||
surfaceContext = CreateContext(_context, context);
|
surfaceContext = CreateContext(_context, context);
|
||||||
_context?.Children.Add(surfaceContext);
|
_context?.Children.Add(surfaceContext);
|
||||||
_contextCache.Add(context, surfaceContext);
|
_contextCache.Add(contextHandle, surfaceContext);
|
||||||
|
|
||||||
context.OnContextCreated(surfaceContext);
|
context.OnContextCreated(surfaceContext);
|
||||||
|
|
||||||
@@ -118,7 +121,8 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if has context in cache
|
// Check if has context in cache
|
||||||
if (_contextCache.TryGetValue(context, out VisjectSurfaceContext surfaceContext))
|
var contextHandle = new ContextHandle(context);
|
||||||
|
if (_contextCache.TryGetValue(contextHandle, out VisjectSurfaceContext surfaceContext))
|
||||||
{
|
{
|
||||||
// Remove from navigation path
|
// Remove from navigation path
|
||||||
while (ContextStack.Contains(surfaceContext))
|
while (ContextStack.Contains(surfaceContext))
|
||||||
@@ -126,7 +130,7 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
// Dispose
|
// Dispose
|
||||||
surfaceContext.Clear();
|
surfaceContext.Clear();
|
||||||
_contextCache.Remove(context);
|
_contextCache.Remove(contextHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +151,8 @@ namespace FlaxEditor.Surface
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if already in a path
|
// Check if already in a path
|
||||||
if (_contextCache.TryGetValue(context, out VisjectSurfaceContext surfaceContext) && ContextStack.Contains(surfaceContext))
|
var contextHandle = new ContextHandle(context);
|
||||||
|
if (_contextCache.TryGetValue(contextHandle, out VisjectSurfaceContext surfaceContext) && ContextStack.Contains(surfaceContext))
|
||||||
{
|
{
|
||||||
// Change stack
|
// Change stack
|
||||||
do
|
do
|
||||||
|
|||||||
@@ -899,6 +899,9 @@ namespace FlaxEditor.Surface
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract byte[] SurfaceData { get; set; }
|
public abstract byte[] SurfaceData { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -366,6 +366,9 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
void ISurfaceContext.OnContextCreated(VisjectSurfaceContext context)
|
void ISurfaceContext.OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -408,6 +408,9 @@ namespace FlaxEditor.Windows
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte[] SurfaceData { get; set; }
|
public byte[] SurfaceData { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -179,6 +179,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract byte[] SurfaceData { get; set; }
|
public abstract byte[] SurfaceData { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1147,6 +1147,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -326,10 +326,10 @@ namespace FlaxEditor.Windows
|
|||||||
Select(item, true);
|
Select(item, true);
|
||||||
|
|
||||||
// Disable scrolling in content view
|
// Disable scrolling in content view
|
||||||
if (_split.Panel2.VScrollBar != null)
|
if (_contentViewPanel.VScrollBar != null)
|
||||||
_split.Panel2.VScrollBar.ThumbEnabled = false;
|
_contentViewPanel.VScrollBar.ThumbEnabled = false;
|
||||||
if (_split.Panel2.HScrollBar != null)
|
if (_contentViewPanel.HScrollBar != null)
|
||||||
_split.Panel2.HScrollBar.ThumbEnabled = false;
|
_contentViewPanel.HScrollBar.ThumbEnabled = false;
|
||||||
ScrollingOnContentView(false);
|
ScrollingOnContentView(false);
|
||||||
|
|
||||||
// Show rename popup
|
// Show rename popup
|
||||||
@@ -340,10 +340,10 @@ namespace FlaxEditor.Windows
|
|||||||
popup.Closed += renamePopup =>
|
popup.Closed += renamePopup =>
|
||||||
{
|
{
|
||||||
// Restore scrolling in content view
|
// Restore scrolling in content view
|
||||||
if (_split.Panel2.VScrollBar != null)
|
if (_contentViewPanel.VScrollBar != null)
|
||||||
_split.Panel2.VScrollBar.ThumbEnabled = true;
|
_contentViewPanel.VScrollBar.ThumbEnabled = true;
|
||||||
if (_split.Panel2.HScrollBar != null)
|
if (_contentViewPanel.HScrollBar != null)
|
||||||
_split.Panel2.HScrollBar.ThumbEnabled = true;
|
_contentViewPanel.HScrollBar.ThumbEnabled = true;
|
||||||
ScrollingOnContentView(true);
|
ScrollingOnContentView(true);
|
||||||
|
|
||||||
// Check if was creating new element
|
// Check if was creating new element
|
||||||
@@ -761,7 +761,7 @@ namespace FlaxEditor.Windows
|
|||||||
|
|
||||||
// Select and scroll to cover in view
|
// Select and scroll to cover in view
|
||||||
_view.Select(item);
|
_view.Select(item);
|
||||||
_split.Panel2.ScrollViewTo(item, fastScroll);
|
_contentViewPanel.ScrollViewTo(item, fastScroll);
|
||||||
|
|
||||||
// Focus
|
// Focus
|
||||||
_view.Focus();
|
_view.Focus();
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ namespace FlaxEngine.Windows.Search
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte[] SurfaceData { get; set; }
|
public byte[] SurfaceData { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnContextCreated(VisjectSurfaceContext context)
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -997,7 +997,7 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
|
|||||||
// Get cached asset info (from registry)
|
// Get cached asset info (from registry)
|
||||||
if (!GetAssetInfo(id, assetInfo))
|
if (!GetAssetInfo(id, assetInfo))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Invalid asset ID ({0}).", id.ToString(Guid::FormatType::N));
|
LOG(Warning, "Invalid or missing asset ({0}, {1}).", id.ToString(Guid::FormatType::N), type.ToString());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ void JsonAssetBase::GetReferences(const StringAnsiView& json, Array<Guid>& outpu
|
|||||||
FindIds(document, output);
|
FindIds(document, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsonAssetBase::Save(const StringView& path)
|
bool JsonAssetBase::Save(const StringView& path) const
|
||||||
{
|
{
|
||||||
// Validate state
|
// Validate state
|
||||||
if (WaitForLoaded())
|
if (WaitForLoaded())
|
||||||
@@ -160,11 +160,32 @@ bool JsonAssetBase::Save(const StringView& path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
|
|
||||||
// Serialize to json file
|
// Serialize to json to the buffer
|
||||||
rapidjson_flax::StringBuffer buffer;
|
rapidjson_flax::StringBuffer buffer;
|
||||||
PrettyJsonWriter writerObj(buffer);
|
PrettyJsonWriter writerObj(buffer);
|
||||||
JsonWriter& writer = writerObj;
|
Save(writerObj);
|
||||||
|
|
||||||
|
// Save json to file
|
||||||
|
if (File::WriteAllBytes(path.HasChars() ? path : StringView(GetPath()), (byte*)buffer.GetString(), (int32)buffer.GetSize()))
|
||||||
|
{
|
||||||
|
LOG(Error, "Cannot save \'{0}\'", ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsonAssetBase::Save(JsonWriter& writer) const
|
||||||
|
{
|
||||||
|
// Validate state
|
||||||
|
if (WaitForLoaded())
|
||||||
|
{
|
||||||
|
LOG(Error, "Asset loading failed. Cannot save it.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ScopeLock lock(Locker);
|
||||||
|
|
||||||
writer.StartObject();
|
writer.StartObject();
|
||||||
{
|
{
|
||||||
// Json resource header
|
// Json resource header
|
||||||
@@ -183,13 +204,6 @@ bool JsonAssetBase::Save(const StringView& path)
|
|||||||
}
|
}
|
||||||
writer.EndObject();
|
writer.EndObject();
|
||||||
|
|
||||||
// Save json to file
|
|
||||||
if (File::WriteAllBytes(path.HasChars() ? path : StringView(GetPath()), (byte*)buffer.GetString(), (int32)buffer.GetSize()))
|
|
||||||
{
|
|
||||||
LOG(Error, "Cannot save \'{0}\'", ToString());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public:
|
|||||||
ISerializable::SerializeDocument Document;
|
ISerializable::SerializeDocument Document;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data node (reference from Document).
|
/// The data node (reference from Document or Document itself).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ISerializable::DeserializeStream* Data;
|
ISerializable::DeserializeStream* Data;
|
||||||
|
|
||||||
@@ -78,7 +78,14 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
|
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
|
||||||
/// <returns>True if cannot save data, otherwise false.</returns>
|
/// <returns>True if cannot save data, otherwise false.</returns>
|
||||||
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
|
API_FUNCTION() bool Save(const StringView& path = StringView::Empty) const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves this asset to the Json Writer buffer (both ID, Typename header and Data contents). Supported only in Editor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The output Json Writer to write asset.</param>
|
||||||
|
/// <returns>True if cannot save data, otherwise false.</returns>
|
||||||
|
bool Save(JsonWriter& writer) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, ISerializable::Deseria
|
|||||||
const ScriptingTypeHandle type = Scripting::FindScriptingType(typeName);
|
const ScriptingTypeHandle type = Scripting::FindScriptingType(typeName);
|
||||||
if (type)
|
if (type)
|
||||||
{
|
{
|
||||||
|
// TODO: cache per-type result in Context to boost loading of the large scenes
|
||||||
|
if (!SceneObject::TypeInitializer.IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
LOG(Warning, "Invalid scene object type {0} (inherits from: {1}).", type.ToString(true), type.GetType().GetBaseType().ToString());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
const ScriptingObjectSpawnParams params(id, type);
|
const ScriptingObjectSpawnParams params(id, type);
|
||||||
obj = (SceneObject*)type.GetType().Script.Spawn(params);
|
obj = (SceneObject*)type.GetType().Script.Spawn(params);
|
||||||
if (obj == nullptr)
|
if (obj == nullptr)
|
||||||
@@ -160,6 +166,10 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, ISerializable::Deseria
|
|||||||
|
|
||||||
void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISerializable::DeserializeStream& stream)
|
void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISerializable::DeserializeStream& stream)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_ASSERTION
|
||||||
|
CHECK(obj);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Check for prefab instance
|
// Check for prefab instance
|
||||||
Guid prefabObjectId;
|
Guid prefabObjectId;
|
||||||
if (JsonTools::GetGuidIfValid(prefabObjectId, stream, "PrefabObjectID"))
|
if (JsonTools::GetGuidIfValid(prefabObjectId, stream, "PrefabObjectID"))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
#include "NetworkStream.h"
|
#include "NetworkStream.h"
|
||||||
|
#include "INetworkSerializable.h"
|
||||||
|
|
||||||
NetworkStream::NetworkStream(const SpawnParams& params)
|
NetworkStream::NetworkStream(const SpawnParams& params)
|
||||||
: ScriptingObject(params)
|
: ScriptingObject(params)
|
||||||
@@ -47,6 +48,26 @@ void NetworkStream::Initialize(byte* buffer, uint32 length)
|
|||||||
_allocated = false;
|
_allocated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkStream::Read(INetworkSerializable& obj)
|
||||||
|
{
|
||||||
|
obj.Deserialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkStream::Read(INetworkSerializable* obj)
|
||||||
|
{
|
||||||
|
obj->Deserialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkStream::Write(INetworkSerializable& obj)
|
||||||
|
{
|
||||||
|
obj.Serialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkStream::Write(INetworkSerializable* obj)
|
||||||
|
{
|
||||||
|
obj->Serialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkStream::Flush()
|
void NetworkStream::Flush()
|
||||||
{
|
{
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "Engine/Serialization/ReadStream.h"
|
#include "Engine/Serialization/ReadStream.h"
|
||||||
#include "Engine/Serialization/WriteStream.h"
|
#include "Engine/Serialization/WriteStream.h"
|
||||||
|
|
||||||
|
class INetworkSerializable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Objects and values serialization stream for sending data over network. Uses memory buffer for both read and write operations.
|
/// Objects and values serialization stream for sending data over network. Uses memory buffer for both read and write operations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -62,6 +64,14 @@ public:
|
|||||||
ReadBytes(data, bytes);
|
ReadBytes(data, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using ReadStream::Read;
|
||||||
|
void Read(INetworkSerializable& obj);
|
||||||
|
void Read(INetworkSerializable* obj);
|
||||||
|
|
||||||
|
using WriteStream::Write;
|
||||||
|
void Write(INetworkSerializable& obj);
|
||||||
|
void Write(INetworkSerializable* obj);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// [Stream]
|
// [Stream]
|
||||||
void Flush() override;
|
void Flush() override;
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ namespace FlaxEngine.GUI
|
|||||||
internal void ChangeChildIndex(Control child, int newIndex)
|
internal void ChangeChildIndex(Control child, int newIndex)
|
||||||
{
|
{
|
||||||
int oldIndex = _children.IndexOf(child);
|
int oldIndex = _children.IndexOf(child);
|
||||||
if (oldIndex == newIndex)
|
if (oldIndex == newIndex || oldIndex == -1)
|
||||||
return;
|
return;
|
||||||
_children.RemoveAt(oldIndex);
|
_children.RemoveAt(oldIndex);
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,25 @@ namespace Flax.Build.Plugins
|
|||||||
public MethodDefinition Execute;
|
public MethodDefinition Execute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct DotnetContext
|
||||||
|
{
|
||||||
|
public bool Modified;
|
||||||
|
public bool Failed;
|
||||||
|
public AssemblyDefinition Assembly;
|
||||||
|
public List<TypeSerializer> AddSerializers;
|
||||||
|
public List<MethodRPC> MethodRPCs;
|
||||||
|
public HashSet<TypeDefinition> GeneratedSerializers;
|
||||||
|
public TypeReference VoidType;
|
||||||
|
public TypeReference NetworkStreamType;
|
||||||
|
}
|
||||||
|
|
||||||
internal const string Network = "Network";
|
internal const string Network = "Network";
|
||||||
internal const string NetworkReplicated = "NetworkReplicated";
|
internal const string NetworkReplicated = "NetworkReplicated";
|
||||||
internal const string NetworkReplicatedAttribute = "FlaxEngine.NetworkReplicatedAttribute";
|
internal const string NetworkReplicatedAttribute = "FlaxEngine.NetworkReplicatedAttribute";
|
||||||
internal const string NetworkRpc = "NetworkRpc";
|
internal const string NetworkRpc = "NetworkRpc";
|
||||||
private const string Thunk1 = "INetworkSerializable_Serialize";
|
private const string Thunk1 = "INetworkSerializable_Serialize";
|
||||||
private const string Thunk2 = "INetworkSerializable_Deserialize";
|
private const string Thunk2 = "INetworkSerializable_Deserialize";
|
||||||
|
|
||||||
private static readonly Dictionary<string, InBuildSerializer> _inBuildSerializers = new Dictionary<string, InBuildSerializer>()
|
private static readonly Dictionary<string, InBuildSerializer> _inBuildSerializers = new Dictionary<string, InBuildSerializer>()
|
||||||
{
|
{
|
||||||
{ "System.Boolean", new InBuildSerializer("WriteBoolean", "ReadBoolean") },
|
{ "System.Boolean", new InBuildSerializer("WriteBoolean", "ReadBoolean") },
|
||||||
@@ -127,6 +140,7 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
fields = structInfo.Fields;
|
fields = structInfo.Fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useReplication = false, useRpc = false;
|
bool useReplication = false, useRpc = false;
|
||||||
if (fields != null)
|
if (fields != null)
|
||||||
{
|
{
|
||||||
@@ -139,6 +153,7 @@ namespace Flax.Build.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties != null)
|
if (properties != null)
|
||||||
{
|
{
|
||||||
foreach (var propertyInfo in properties)
|
foreach (var propertyInfo in properties)
|
||||||
@@ -150,6 +165,7 @@ namespace Flax.Build.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functions != null)
|
if (functions != null)
|
||||||
{
|
{
|
||||||
foreach (var functionInfo in functions)
|
foreach (var functionInfo in functions)
|
||||||
@@ -161,20 +177,22 @@ namespace Flax.Build.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useReplication)
|
if (useReplication)
|
||||||
{
|
{
|
||||||
typeInfo.SetTag(NetworkReplicated, string.Empty);
|
typeInfo.SetTag(NetworkReplicated, string.Empty);
|
||||||
|
|
||||||
// Generate C++ wrapper functions to serialize/deserialize type
|
// Generate C++ wrapper functions to serialize/deserialize type
|
||||||
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkReplicator.h");
|
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkReplicator.h");
|
||||||
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkStream.h");
|
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkStream.h");
|
||||||
OnGenerateCppTypeSerialize(buildData, typeInfo, contents, fields, properties, true);
|
OnGenerateCppTypeSerialize(buildData, typeInfo, contents, fields, properties, true);
|
||||||
OnGenerateCppTypeSerialize(buildData, typeInfo, contents, fields, properties, false);
|
OnGenerateCppTypeSerialize(buildData, typeInfo, contents, fields, properties, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useRpc)
|
if (useRpc)
|
||||||
{
|
{
|
||||||
typeInfo.SetTag(NetworkRpc, string.Empty);
|
typeInfo.SetTag(NetworkRpc, string.Empty);
|
||||||
|
|
||||||
// Generate C++ wrapper functions to invoke/execute RPC
|
// Generate C++ wrapper functions to invoke/execute RPC
|
||||||
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkStream.h");
|
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkStream.h");
|
||||||
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkReplicator.h");
|
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkReplicator.h");
|
||||||
@@ -282,7 +300,7 @@ namespace Flax.Build.Plugins
|
|||||||
// Replicate base type
|
// Replicate base type
|
||||||
OnGenerateCppWriteSerializer(contents, classStructInfo.BaseType.NativeName, "obj", serialize);
|
OnGenerateCppWriteSerializer(contents, classStructInfo.BaseType.NativeName, "obj", serialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replicate all marked fields and properties
|
// Replicate all marked fields and properties
|
||||||
if (fields != null)
|
if (fields != null)
|
||||||
{
|
{
|
||||||
@@ -293,6 +311,7 @@ namespace Flax.Build.Plugins
|
|||||||
OnGenerateCppTypeSerializeData(buildData, typeInfo, contents, fieldInfo.Type, $"obj.{fieldInfo.Name}", serialize);
|
OnGenerateCppTypeSerializeData(buildData, typeInfo, contents, fieldInfo.Type, $"obj.{fieldInfo.Name}", serialize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties != null)
|
if (properties != null)
|
||||||
{
|
{
|
||||||
foreach (var propertyInfo in properties)
|
foreach (var propertyInfo in properties)
|
||||||
@@ -308,6 +327,7 @@ namespace Flax.Build.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contents.AppendLine(" }");
|
contents.AppendLine(" }");
|
||||||
contents.AppendLine();
|
contents.AppendLine();
|
||||||
}
|
}
|
||||||
@@ -326,9 +346,10 @@ namespace Flax.Build.Plugins
|
|||||||
if (apiType != null)
|
if (apiType != null)
|
||||||
return IsRawPOD(buildData, apiType);
|
return IsRawPOD(buildData, apiType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGenerateCppTypeSerializeData(Builder.BuildData buildData, ApiTypeInfo caller, StringBuilder contents, TypeInfo type, string name, bool serialize)
|
private void OnGenerateCppTypeSerializeData(Builder.BuildData buildData, ApiTypeInfo caller, StringBuilder contents, TypeInfo type, string name, bool serialize)
|
||||||
{
|
{
|
||||||
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, type, caller);
|
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, type, caller);
|
||||||
@@ -365,7 +386,7 @@ namespace Flax.Build.Plugins
|
|||||||
throw new Exception($"Invalid pointer type '{type}' that cannot be serialized for replication of {caller.Name}.");
|
throw new Exception($"Invalid pointer type '{type}' that cannot be serialized for replication of {caller.Name}.");
|
||||||
if (type.IsRef)
|
if (type.IsRef)
|
||||||
throw new Exception($"Invalid reference type '{type}' that cannot be serialized for replication of {caller.Name}.");
|
throw new Exception($"Invalid reference type '{type}' that cannot be serialized for replication of {caller.Name}.");
|
||||||
|
|
||||||
// Structure serializer
|
// Structure serializer
|
||||||
OnGenerateCppWriteSerializer(contents, apiType.NativeName, name, serialize);
|
OnGenerateCppWriteSerializer(contents, apiType.NativeName, name, serialize);
|
||||||
}
|
}
|
||||||
@@ -375,13 +396,13 @@ namespace Flax.Build.Plugins
|
|||||||
OnGenerateCppWriteRaw(contents, name, serialize);
|
OnGenerateCppWriteRaw(contents, name, serialize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGenerateCppWriteRaw(StringBuilder contents, string data, bool serialize)
|
private void OnGenerateCppWriteRaw(StringBuilder contents, string data, bool serialize)
|
||||||
{
|
{
|
||||||
var method = serialize ? "Write" : "Read";
|
var method = serialize ? "Write" : "Read";
|
||||||
contents.AppendLine($" stream->{method}({data});");
|
contents.AppendLine($" stream->{method}({data});");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGenerateCppWriteSerializer(StringBuilder contents, string type, string data, bool serialize)
|
private void OnGenerateCppWriteSerializer(StringBuilder contents, string type, string data, bool serialize)
|
||||||
{
|
{
|
||||||
if (type == "ScriptingObject" || type == "Script" || type == "Actor")
|
if (type == "ScriptingObject" || type == "Script" || type == "Actor")
|
||||||
@@ -405,6 +426,7 @@ namespace Flax.Build.Plugins
|
|||||||
// Register generated serializer functions
|
// Register generated serializer functions
|
||||||
contents.AppendLine($" NetworkReplicator::AddSerializer(ScriptingTypeHandle({typeNameNative}::TypeInitializer), {typeNameInternal}Internal::INetworkSerializable_Serialize, {typeNameInternal}Internal::INetworkSerializable_Deserialize);");
|
contents.AppendLine($" NetworkReplicator::AddSerializer(ScriptingTypeHandle({typeNameNative}::TypeInitializer), {typeNameInternal}Internal::INetworkSerializable_Serialize, {typeNameInternal}Internal::INetworkSerializable_Deserialize);");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rpcTag != null)
|
if (rpcTag != null)
|
||||||
{
|
{
|
||||||
// Register generated RPCs
|
// Register generated RPCs
|
||||||
@@ -413,6 +435,7 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
functions = classInfo.Functions;
|
functions = classInfo.Functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functions != null)
|
if (functions != null)
|
||||||
{
|
{
|
||||||
foreach (var functionInfo in functions)
|
foreach (var functionInfo in functions)
|
||||||
@@ -430,16 +453,15 @@ namespace Flax.Build.Plugins
|
|||||||
// Skip types that don't use networking
|
// Skip types that don't use networking
|
||||||
if (typeInfo.GetTag(NetworkReplicated) == null)
|
if (typeInfo.GetTag(NetworkReplicated) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (typeInfo is ClassInfo)
|
if (typeInfo is ClassInfo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Generate C# wrapper functions to serialize/deserialize type directly from managed code
|
// Generate C# wrapper functions to serialize/deserialize type directly from managed code
|
||||||
OnGenerateCSharpTypeSerialize(buildData, typeInfo, contents, indent, true);
|
OnGenerateCSharpTypeSerialize(buildData, typeInfo, contents, indent, true);
|
||||||
OnGenerateCSharpTypeSerialize(buildData, typeInfo, contents, indent, false);
|
OnGenerateCSharpTypeSerialize(buildData, typeInfo, contents, indent, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGenerateCSharpTypeSerialize(Builder.BuildData buildData, ApiTypeInfo typeInfo, StringBuilder contents, string indent, bool serialize)
|
private void OnGenerateCSharpTypeSerialize(Builder.BuildData buildData, ApiTypeInfo typeInfo, StringBuilder contents, string indent, bool serialize)
|
||||||
{
|
{
|
||||||
var mode = serialize ? "true" : "false";
|
var mode = serialize ? "true" : "false";
|
||||||
@@ -463,6 +485,7 @@ namespace Flax.Build.Plugins
|
|||||||
contents.Append(indent).AppendLine($" throw new NotImplementedException(\"Not supported native structure with references used in managed code for replication.\");");
|
contents.Append(indent).AppendLine($" throw new NotImplementedException(\"Not supported native structure with references used in managed code for replication.\");");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contents.Append(indent).AppendLine("}");
|
contents.Append(indent).AppendLine("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +516,7 @@ namespace Flax.Build.Plugins
|
|||||||
private void OnPatchDotNetAssembly(Builder.BuildData buildData, NativeCpp.BuildOptions buildOptions, Task buildTask, string assemblyPath)
|
private void OnPatchDotNetAssembly(Builder.BuildData buildData, NativeCpp.BuildOptions buildOptions, Task buildTask, string assemblyPath)
|
||||||
{
|
{
|
||||||
using (DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver())
|
using (DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver())
|
||||||
using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters{ ReadWrite = true, ReadSymbols = true, AssemblyResolver = assemblyResolver }))
|
using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = assemblyResolver }))
|
||||||
{
|
{
|
||||||
// Setup module search locations
|
// Setup module search locations
|
||||||
var searchDirectories = new HashSet<string>();
|
var searchDirectories = new HashSet<string>();
|
||||||
@@ -503,107 +526,36 @@ namespace Flax.Build.Plugins
|
|||||||
if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
||||||
searchDirectories.Add(Path.GetDirectoryName(file));
|
searchDirectories.Add(Path.GetDirectoryName(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var e in searchDirectories)
|
foreach (var e in searchDirectories)
|
||||||
assemblyResolver.AddSearchDirectory(e);
|
assemblyResolver.AddSearchDirectory(e);
|
||||||
|
|
||||||
ModuleDefinition module = assembly.MainModule;
|
ModuleDefinition module = assembly.MainModule;
|
||||||
TypeReference voidType = module.ImportReference(typeof(void));
|
|
||||||
module.GetType("FlaxEngine.Networking.NetworkStream", out var networkStreamType);
|
|
||||||
|
|
||||||
// Process all types within a module
|
// Process all types within a module
|
||||||
bool modified = false;
|
var context = new DotnetContext
|
||||||
bool failed = false;
|
{
|
||||||
var addSerializers = new List<TypeSerializer>();
|
Modified = false,
|
||||||
var methodRPCs = new List<MethodRPC>();
|
Failed = false,
|
||||||
|
Assembly = assembly,
|
||||||
|
AddSerializers = new List<TypeSerializer>(),
|
||||||
|
MethodRPCs = new List<MethodRPC>(),
|
||||||
|
GeneratedSerializers = new HashSet<TypeDefinition>(),
|
||||||
|
VoidType = module.ImportReference(typeof(void)),
|
||||||
|
};
|
||||||
|
module.GetType("FlaxEngine.Networking.NetworkStream", out context.NetworkStreamType);
|
||||||
foreach (TypeDefinition type in module.Types)
|
foreach (TypeDefinition type in module.Types)
|
||||||
{
|
{
|
||||||
if (type.IsInterface || type.IsEnum)
|
GenerateTypeNetworking(ref context, type);
|
||||||
continue;
|
|
||||||
var isNative = type.HasAttribute("FlaxEngine.UnmanagedAttribute");
|
|
||||||
if (isNative)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Generate RPCs
|
|
||||||
var methods = type.Methods;
|
|
||||||
var methodsCount = methods.Count; // methods list can be modified during RPCs generation
|
|
||||||
for (int i = 0; i < methodsCount; i++)
|
|
||||||
{
|
|
||||||
MethodDefinition method = methods[i];
|
|
||||||
var attribute = method.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == "FlaxEngine.NetworkRpcAttribute");
|
|
||||||
if (attribute != null)
|
|
||||||
{
|
|
||||||
GenerateDotNetRPCBody(type, method, attribute, ref failed, networkStreamType, methodRPCs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate serializers
|
|
||||||
if (type.HasMethod(Thunk1) || type.HasMethod(Thunk2))
|
|
||||||
continue;
|
|
||||||
var isINetworkSerializable = type.HasInterface("FlaxEngine.Networking.INetworkSerializable");
|
|
||||||
MethodDefinition serializeINetworkSerializable = null, deserializeINetworkSerializable = null;
|
|
||||||
if (isINetworkSerializable)
|
|
||||||
{
|
|
||||||
foreach (MethodDefinition m in type.Methods)
|
|
||||||
{
|
|
||||||
if (m.HasBody && m.Parameters.Count == 1 && m.Parameters[0].ParameterType.FullName == "FlaxEngine.Networking.NetworkStream")
|
|
||||||
{
|
|
||||||
if (m.Name == "Serialize")
|
|
||||||
serializeINetworkSerializable = m;
|
|
||||||
else if (m.Name == "Deserialize")
|
|
||||||
deserializeINetworkSerializable = m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var isNetworkReplicated = false;
|
|
||||||
foreach (FieldDefinition f in type.Fields)
|
|
||||||
{
|
|
||||||
if (!f.HasAttribute(NetworkReplicatedAttribute))
|
|
||||||
continue;
|
|
||||||
isNetworkReplicated = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
foreach (PropertyDefinition p in type.Properties)
|
|
||||||
{
|
|
||||||
if (!p.HasAttribute(NetworkReplicatedAttribute))
|
|
||||||
continue;
|
|
||||||
isNetworkReplicated = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (type.IsValueType)
|
|
||||||
{
|
|
||||||
if (isINetworkSerializable)
|
|
||||||
{
|
|
||||||
// Generate INetworkSerializable interface method calls
|
|
||||||
GenerateCallINetworkSerializable(type, Thunk1, voidType, networkStreamType, serializeINetworkSerializable);
|
|
||||||
GenerateCallINetworkSerializable(type, Thunk2, voidType, networkStreamType, deserializeINetworkSerializable);
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
else if (isNetworkReplicated)
|
|
||||||
{
|
|
||||||
// Generate serialization methods
|
|
||||||
GenerateSerializer(type, true, ref failed, Thunk1, voidType, networkStreamType);
|
|
||||||
GenerateSerializer(type, false, ref failed, Thunk2, voidType, networkStreamType);
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!isINetworkSerializable && isNetworkReplicated)
|
|
||||||
{
|
|
||||||
// Generate serialization methods
|
|
||||||
var addSerializer = new TypeSerializer();
|
|
||||||
addSerializer.Type = type;
|
|
||||||
addSerializer.Serialize = GenerateNativeSerializer(type, true, ref failed, Thunk1, voidType, networkStreamType);
|
|
||||||
addSerializer.Deserialize = GenerateNativeSerializer(type, false, ref failed, Thunk2, voidType, networkStreamType);
|
|
||||||
addSerializers.Add(addSerializer);
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (failed)
|
|
||||||
|
if (context.Failed)
|
||||||
throw new Exception($"Failed to generate network replication for assembly {assemblyPath}");
|
throw new Exception($"Failed to generate network replication for assembly {assemblyPath}");
|
||||||
if (!modified)
|
if (!context.Modified)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Generate serializers initializer (invoked on module load)
|
// Generate serializers initializer (invoked on module load)
|
||||||
if (addSerializers.Count != 0 || methodRPCs.Count != 0)
|
if (context.AddSerializers.Count != 0 || context.MethodRPCs.Count != 0)
|
||||||
{
|
{
|
||||||
// Create class
|
// Create class
|
||||||
var name = "Initializer";
|
var name = "Initializer";
|
||||||
@@ -623,7 +575,7 @@ namespace Flax.Build.Plugins
|
|||||||
c.CustomAttributes.Add(attribute);
|
c.CustomAttributes.Add(attribute);
|
||||||
|
|
||||||
// Add Init method
|
// Add Init method
|
||||||
var m = new MethodDefinition("Init", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, voidType);
|
var m = new MethodDefinition("Init", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, context.VoidType);
|
||||||
ILProcessor il = m.Body.GetILProcessor();
|
ILProcessor il = m.Body.GetILProcessor();
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
module.GetType("System.Type", out var typeType);
|
module.GetType("System.Type", out var typeType);
|
||||||
@@ -637,7 +589,7 @@ namespace Flax.Build.Plugins
|
|||||||
module.ImportReference(addRPC);
|
module.ImportReference(addRPC);
|
||||||
var executeRPCFuncType = addRPC.Parameters[2].ParameterType;
|
var executeRPCFuncType = addRPC.Parameters[2].ParameterType;
|
||||||
var executeRPCFuncCtor = executeRPCFuncType.Resolve().GetMethod(".ctor");
|
var executeRPCFuncCtor = executeRPCFuncType.Resolve().GetMethod(".ctor");
|
||||||
foreach (var e in addSerializers)
|
foreach (var e in context.AddSerializers)
|
||||||
{
|
{
|
||||||
// NetworkReplicator.AddSerializer(typeof(<type>), <type>.INetworkSerializable_SerializeNative, <type>.INetworkSerializable_DeserializeNative);
|
// NetworkReplicator.AddSerializer(typeof(<type>), <type>.INetworkSerializable_SerializeNative, <type>.INetworkSerializable_DeserializeNative);
|
||||||
il.Emit(OpCodes.Ldtoken, e.Type);
|
il.Emit(OpCodes.Ldtoken, e.Type);
|
||||||
@@ -650,7 +602,8 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Newobj, module.ImportReference(serializeFuncCtor));
|
il.Emit(OpCodes.Newobj, module.ImportReference(serializeFuncCtor));
|
||||||
il.Emit(OpCodes.Call, module.ImportReference(addSerializer));
|
il.Emit(OpCodes.Call, module.ImportReference(addSerializer));
|
||||||
}
|
}
|
||||||
foreach (var e in methodRPCs)
|
|
||||||
|
foreach (var e in context.MethodRPCs)
|
||||||
{
|
{
|
||||||
// NetworkReplicator.AddRPC(typeof(<type>), "<name>", <name>_Execute, <isServer>, <isClient>, <channel>);
|
// NetworkReplicator.AddRPC(typeof(<type>), "<name>", <name>_Execute, <isServer>, <isClient>, <channel>);
|
||||||
il.Emit(OpCodes.Ldtoken, e.Type);
|
il.Emit(OpCodes.Ldtoken, e.Type);
|
||||||
@@ -664,20 +617,135 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Ldc_I4, e.Channel);
|
il.Emit(OpCodes.Ldc_I4, e.Channel);
|
||||||
il.Emit(OpCodes.Call, module.ImportReference(addRPC));
|
il.Emit(OpCodes.Call, module.ImportReference(addRPC));
|
||||||
}
|
}
|
||||||
|
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Ret);
|
il.Emit(OpCodes.Ret);
|
||||||
c.Methods.Add(m);
|
c.Methods.Add(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize assembly back to the file
|
// Serialize assembly back to the file
|
||||||
assembly.Write(new WriterParameters { WriteSymbols = true } );
|
assembly.Write(new WriterParameters { WriteSymbols = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateCallINetworkSerializable(TypeDefinition type, string name, TypeReference voidType, TypeReference networkStreamType, MethodDefinition method)
|
private static void GenerateTypeNetworking(ref DotnetContext context, TypeDefinition type)
|
||||||
{
|
{
|
||||||
var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, voidType);
|
if (type.IsInterface || type.IsEnum)
|
||||||
m.Parameters.Add(new ParameterDefinition("stream", ParameterAttributes.None, networkStreamType));
|
return;
|
||||||
|
|
||||||
|
// Process nested types
|
||||||
|
foreach (var nestedType in type.NestedTypes)
|
||||||
|
{
|
||||||
|
GenerateTypeNetworking(ref context, nestedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsClass)
|
||||||
|
{
|
||||||
|
// Generate RPCs
|
||||||
|
var methods = type.Methods;
|
||||||
|
var methodsCount = methods.Count; // methods list can be modified during RPCs generation
|
||||||
|
for (int i = 0; i < methodsCount; i++)
|
||||||
|
{
|
||||||
|
MethodDefinition method = methods[i];
|
||||||
|
var attribute = method.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == "FlaxEngine.NetworkRpcAttribute");
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
GenerateDotNetRPCBody(ref context, type, method, attribute, context.NetworkStreamType);
|
||||||
|
context.Modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateTypeSerialization(ref context, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateTypeSerialization(ref DotnetContext context, TypeDefinition type)
|
||||||
|
{
|
||||||
|
// Skip types outside from current assembly
|
||||||
|
if (context.Assembly.MainModule != type.Module)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Skip if already generated serialization for this type (eg. via referenced RPC in other type)
|
||||||
|
if (context.GeneratedSerializers.Contains(type))
|
||||||
|
return;
|
||||||
|
context.GeneratedSerializers.Add(type);
|
||||||
|
|
||||||
|
// Skip native types
|
||||||
|
var isNative = type.HasAttribute("FlaxEngine.UnmanagedAttribute");
|
||||||
|
if (isNative)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Skip if manually implemented serializers
|
||||||
|
if (type.HasMethod(Thunk1) || type.HasMethod(Thunk2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Generate serializers
|
||||||
|
var isINetworkSerializable = type.HasInterface("FlaxEngine.Networking.INetworkSerializable");
|
||||||
|
MethodDefinition serializeINetworkSerializable = null, deserializeINetworkSerializable = null;
|
||||||
|
if (isINetworkSerializable)
|
||||||
|
{
|
||||||
|
foreach (MethodDefinition m in type.Methods)
|
||||||
|
{
|
||||||
|
if (m.HasBody && m.Parameters.Count == 1 && m.Parameters[0].ParameterType.FullName == "FlaxEngine.Networking.NetworkStream")
|
||||||
|
{
|
||||||
|
if (m.Name == "Serialize")
|
||||||
|
serializeINetworkSerializable = m;
|
||||||
|
else if (m.Name == "Deserialize")
|
||||||
|
deserializeINetworkSerializable = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isNetworkReplicated = false;
|
||||||
|
foreach (FieldDefinition f in type.Fields)
|
||||||
|
{
|
||||||
|
if (!f.HasAttribute(NetworkReplicatedAttribute))
|
||||||
|
continue;
|
||||||
|
isNetworkReplicated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (PropertyDefinition p in type.Properties)
|
||||||
|
{
|
||||||
|
if (!p.HasAttribute(NetworkReplicatedAttribute))
|
||||||
|
continue;
|
||||||
|
isNetworkReplicated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsValueType)
|
||||||
|
{
|
||||||
|
if (isINetworkSerializable)
|
||||||
|
{
|
||||||
|
// Generate INetworkSerializable interface method calls
|
||||||
|
GenerateCallINetworkSerializable(ref context, type, Thunk1, serializeINetworkSerializable);
|
||||||
|
GenerateCallINetworkSerializable(ref context, type, Thunk2, deserializeINetworkSerializable);
|
||||||
|
context.Modified = true;
|
||||||
|
}
|
||||||
|
else if (isNetworkReplicated)
|
||||||
|
{
|
||||||
|
// Generate serialization methods
|
||||||
|
GenerateSerializer(ref context, type, true, Thunk1);
|
||||||
|
GenerateSerializer(ref context, type, false, Thunk2);
|
||||||
|
context.Modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!isINetworkSerializable && isNetworkReplicated)
|
||||||
|
{
|
||||||
|
// Generate serialization methods
|
||||||
|
var addSerializer = new TypeSerializer();
|
||||||
|
addSerializer.Type = type;
|
||||||
|
addSerializer.Serialize = GenerateNativeSerializer(ref context, type, true, Thunk1);
|
||||||
|
addSerializer.Deserialize = GenerateNativeSerializer(ref context, type, false, Thunk2);
|
||||||
|
context.AddSerializers.Add(addSerializer);
|
||||||
|
context.Modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateCallINetworkSerializable(ref DotnetContext context, TypeDefinition type, string name, MethodDefinition method)
|
||||||
|
{
|
||||||
|
var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, context.VoidType);
|
||||||
|
m.Parameters.Add(new ParameterDefinition("stream", ParameterAttributes.None, context.NetworkStreamType));
|
||||||
ILProcessor il = m.Body.GetILProcessor();
|
ILProcessor il = m.Body.GetILProcessor();
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Ldarg_0);
|
il.Emit(OpCodes.Ldarg_0);
|
||||||
@@ -688,15 +756,14 @@ namespace Flax.Build.Plugins
|
|||||||
type.Methods.Add(m);
|
type.Methods.Add(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodDefinition GenerateSerializer(TypeDefinition type, bool serialize, ref bool failed, string name, TypeReference voidType, TypeReference networkStreamType)
|
private static MethodDefinition GenerateSerializer(ref DotnetContext context, TypeDefinition type, bool serialize, string name)
|
||||||
{
|
{
|
||||||
ModuleDefinition module = type.Module;
|
ModuleDefinition module = type.Module;
|
||||||
var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, voidType);
|
var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, context.VoidType);
|
||||||
m.Parameters.Add(new ParameterDefinition("stream", ParameterAttributes.None, module.ImportReference(networkStreamType)));
|
m.Parameters.Add(new ParameterDefinition("stream", ParameterAttributes.None, module.ImportReference(context.NetworkStreamType)));
|
||||||
TypeDefinition networkStream = networkStreamType.Resolve();
|
|
||||||
ILProcessor il = m.Body.GetILProcessor();
|
ILProcessor il = m.Body.GetILProcessor();
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
|
|
||||||
// Serialize base type
|
// Serialize base type
|
||||||
if (type.BaseType != null && type.BaseType.FullName != "System.ValueType" && type.BaseType.FullName != "FlaxEngine.Object" && type.BaseType.CanBeResolved())
|
if (type.BaseType != null && type.BaseType.FullName != "System.ValueType" && type.BaseType.FullName != "FlaxEngine.Object" && type.BaseType.CanBeResolved())
|
||||||
{
|
{
|
||||||
@@ -708,7 +775,7 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
if (!f.HasAttribute(NetworkReplicatedAttribute))
|
if (!f.HasAttribute(NetworkReplicatedAttribute))
|
||||||
continue;
|
continue;
|
||||||
GenerateSerializerType(type, serialize, ref failed, f, null, f.FieldType, il, networkStream);
|
GenerateSerializerType(ref context, type, serialize, f, null, f.FieldType, il);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize all type properties marked with NetworkReplicated attribute
|
// Serialize all type properties marked with NetworkReplicated attribute
|
||||||
@@ -716,7 +783,7 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
if (!p.HasAttribute(NetworkReplicatedAttribute))
|
if (!p.HasAttribute(NetworkReplicatedAttribute))
|
||||||
continue;
|
continue;
|
||||||
GenerateSerializerType(type, serialize, ref failed, null, p, p.PropertyType, il, networkStream);
|
GenerateSerializerType(ref context, type, serialize, null, p, p.PropertyType, il);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serialize)
|
if (serialize)
|
||||||
@@ -726,17 +793,17 @@ namespace Flax.Build.Plugins
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodDefinition GenerateNativeSerializer(TypeDefinition type, bool serialize, ref bool failed, string name, TypeReference voidType, TypeReference networkStreamType)
|
private static MethodDefinition GenerateNativeSerializer(ref DotnetContext context, TypeDefinition type, bool serialize, string name)
|
||||||
{
|
{
|
||||||
ModuleDefinition module = type.Module;
|
ModuleDefinition module = type.Module;
|
||||||
module.GetType("System.IntPtr", out var intPtrType);
|
module.GetType("System.IntPtr", out var intPtrType);
|
||||||
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
||||||
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
|
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
|
||||||
|
|
||||||
var m = new MethodDefinition(name + "Native", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, voidType);
|
var m = new MethodDefinition(name + "Native", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, context.VoidType);
|
||||||
m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType));
|
m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType));
|
||||||
m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtrType));
|
m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtrType));
|
||||||
TypeReference networkStream = module.ImportReference(networkStreamType);
|
TypeReference networkStream = module.ImportReference(context.NetworkStreamType);
|
||||||
ILProcessor il = m.Body.GetILProcessor();
|
ILProcessor il = m.Body.GetILProcessor();
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
il.Body.InitLocals = true;
|
il.Body.InitLocals = true;
|
||||||
@@ -747,16 +814,16 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
|
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
|
||||||
il.Emit(OpCodes.Castclass, type);
|
il.Emit(OpCodes.Castclass, type);
|
||||||
il.Emit(OpCodes.Stloc_0);
|
il.Emit(OpCodes.Stloc_0);
|
||||||
|
|
||||||
// NetworkStream stream = (NetworkStream)FlaxEngine.Object.FromUnmanagedPtr(streamPtr)
|
// NetworkStream stream = (NetworkStream)FlaxEngine.Object.FromUnmanagedPtr(streamPtr)
|
||||||
il.Body.Variables.Add(new VariableDefinition(networkStream));
|
il.Body.Variables.Add(new VariableDefinition(networkStream));
|
||||||
il.Emit(OpCodes.Ldarg_1);
|
il.Emit(OpCodes.Ldarg_1);
|
||||||
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
|
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
|
||||||
il.Emit(OpCodes.Castclass, module.ImportReference(networkStream));
|
il.Emit(OpCodes.Castclass, module.ImportReference(networkStream));
|
||||||
il.Emit(OpCodes.Stloc_1);
|
il.Emit(OpCodes.Stloc_1);
|
||||||
|
|
||||||
// Generate normal serializer
|
// Generate normal serializer
|
||||||
var serializer = GenerateSerializer(type, serialize, ref failed, name, voidType, networkStreamType);
|
var serializer = GenerateSerializer(ref context, type, serialize, name);
|
||||||
|
|
||||||
// Call serializer
|
// Call serializer
|
||||||
il.Emit(OpCodes.Ldloc_0);
|
il.Emit(OpCodes.Ldloc_0);
|
||||||
@@ -796,10 +863,11 @@ namespace Flax.Build.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateSerializerType(TypeDefinition type, bool serialize, ref bool failed, FieldReference field, PropertyDefinition property, TypeReference valueType, ILProcessor il, TypeDefinition networkStreamType)
|
private static void GenerateSerializerType(ref DotnetContext context, TypeDefinition type, bool serialize, FieldReference field, PropertyDefinition property, TypeReference valueType, ILProcessor il)
|
||||||
{
|
{
|
||||||
if (field == null && property == null)
|
if (field == null && property == null)
|
||||||
throw new ArgumentException();
|
throw new ArgumentException();
|
||||||
|
TypeDefinition networkStreamType = context.NetworkStreamType.Resolve();
|
||||||
var propertyGetOpCode = OpCodes.Call;
|
var propertyGetOpCode = OpCodes.Call;
|
||||||
var propertySetOpCode = OpCodes.Call;
|
var propertySetOpCode = OpCodes.Call;
|
||||||
if (property != null)
|
if (property != null)
|
||||||
@@ -807,22 +875,29 @@ namespace Flax.Build.Plugins
|
|||||||
if (property.GetMethod == null)
|
if (property.GetMethod == null)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
MonoCecil.CompilationError($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.SetMethod == null)
|
if (property.SetMethod == null)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
MonoCecil.CompilationError($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.GetMethod.IsVirtual)
|
if (property.GetMethod.IsVirtual)
|
||||||
propertyGetOpCode = OpCodes.Callvirt;
|
propertyGetOpCode = OpCodes.Callvirt;
|
||||||
if (property.SetMethod.IsVirtual)
|
if (property.SetMethod.IsVirtual)
|
||||||
propertySetOpCode = OpCodes.Callvirt;
|
propertySetOpCode = OpCodes.Callvirt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleDefinition module = type.Module;
|
ModuleDefinition module = type.Module;
|
||||||
TypeDefinition valueTypeDef = valueType.Resolve();
|
TypeDefinition valueTypeDef = valueType.Resolve();
|
||||||
|
|
||||||
|
// Ensure to have valid serialization already generated for that value type (eg. when using custom structure field serialization)
|
||||||
|
GenerateTypeSerialization(ref context, valueTypeDef);
|
||||||
|
|
||||||
if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
||||||
{
|
{
|
||||||
// Call NetworkStream method to write/read data
|
// Call NetworkStream method to write/read data
|
||||||
@@ -843,6 +918,7 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Ldarg_1);
|
il.Emit(OpCodes.Ldarg_1);
|
||||||
m = networkStreamType.GetMethod(serializer.ReadMethod);
|
m = networkStreamType.GetMethod(serializer.ReadMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||||
if (!serialize)
|
if (!serialize)
|
||||||
{
|
{
|
||||||
@@ -966,7 +1042,7 @@ namespace Flax.Build.Plugins
|
|||||||
else
|
else
|
||||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||||
|
|
||||||
// int num2 = ((array2 != null) ? array2.Length : 0);
|
// int num2 = ((array2 != null) ? array2.Length : 0);
|
||||||
il.Emit(OpCodes.Dup);
|
il.Emit(OpCodes.Dup);
|
||||||
Instruction jmp1 = il.Create(OpCodes.Nop);
|
Instruction jmp1 = il.Create(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Brtrue_S, jmp1);
|
il.Emit(OpCodes.Brtrue_S, jmp1);
|
||||||
@@ -979,8 +1055,8 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Conv_I4);
|
il.Emit(OpCodes.Conv_I4);
|
||||||
il.Append(jmp2);
|
il.Append(jmp2);
|
||||||
il.Emit(OpCodes.Stloc, varStart + 0);
|
il.Emit(OpCodes.Stloc, varStart + 0);
|
||||||
|
|
||||||
// stream.WriteInt32(num2);
|
// stream.WriteInt32(num2);
|
||||||
il.Emit(OpCodes.Ldarg_1);
|
il.Emit(OpCodes.Ldarg_1);
|
||||||
il.Emit(OpCodes.Ldloc, varStart + 0);
|
il.Emit(OpCodes.Ldloc, varStart + 0);
|
||||||
var m = networkStreamType.GetMethod("WriteInt32");
|
var m = networkStreamType.GetMethod("WriteInt32");
|
||||||
@@ -1039,16 +1115,16 @@ namespace Flax.Build.Plugins
|
|||||||
var m = networkStreamType.GetMethod("ReadInt32");
|
var m = networkStreamType.GetMethod("ReadInt32");
|
||||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||||
il.Emit(OpCodes.Stloc, varStart + 0);
|
il.Emit(OpCodes.Stloc, varStart + 0);
|
||||||
|
|
||||||
// System.Array.Resize(ref Array1, num);
|
// System.Array.Resize(ref Array1, num);
|
||||||
il.Emit(OpCodes.Ldarg_0);
|
il.Emit(OpCodes.Ldarg_0);
|
||||||
il.Emit(OpCodes.Ldflda, field);
|
il.Emit(OpCodes.Ldflda, field);
|
||||||
il.Emit(OpCodes.Ldloc, varStart + 0);
|
il.Emit(OpCodes.Ldloc, varStart + 0);
|
||||||
module.TryGetTypeReference("System.Array", out var arrayType);
|
module.TryGetTypeReference("System.Array", out var arrayType);
|
||||||
m = arrayType.Resolve().GetMethod("Resize", 2);
|
m = arrayType.Resolve().GetMethod("Resize", 2);
|
||||||
il.Emit(OpCodes.Call, module.ImportReference(m.InflateGeneric(elementType)));
|
il.Emit(OpCodes.Call, module.ImportReference(m.InflateGeneric(elementType)));
|
||||||
|
|
||||||
// fixed (int* buffer = Array1)
|
// fixed (int* buffer = Array1)
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Ldarg_0);
|
il.Emit(OpCodes.Ldarg_0);
|
||||||
il.Emit(OpCodes.Ldfld, field);
|
il.Emit(OpCodes.Ldfld, field);
|
||||||
@@ -1067,8 +1143,8 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Stloc, varStart + 1);
|
il.Emit(OpCodes.Stloc, varStart + 1);
|
||||||
Instruction jmp3 = il.Create(OpCodes.Nop);
|
Instruction jmp3 = il.Create(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Br_S, jmp3);
|
il.Emit(OpCodes.Br_S, jmp3);
|
||||||
|
|
||||||
// stream.ReadBytes((byte*)buffer, num * sizeof(<elementType>));
|
// stream.ReadBytes((byte*)buffer, num * sizeof(<elementType>));
|
||||||
il.Append(jmp2);
|
il.Append(jmp2);
|
||||||
il.Emit(OpCodes.Ldloc, varStart + 2);
|
il.Emit(OpCodes.Ldloc, varStart + 2);
|
||||||
il.Emit(OpCodes.Ldc_I4_0);
|
il.Emit(OpCodes.Ldc_I4_0);
|
||||||
@@ -1097,14 +1173,18 @@ namespace Flax.Build.Plugins
|
|||||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {field.Name} in {type.FullName} for automatic replication.", field.Resolve());
|
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {field.Name} in {type.FullName} for automatic replication.", field.Resolve());
|
||||||
else
|
else
|
||||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' for automatic replication.");
|
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' for automatic replication.");
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateDotNetRPCSerializerType(TypeDefinition type, bool serialize, ref bool failed, int localIndex, TypeReference valueType, ILProcessor il, TypeDefinition networkStreamType, int streamLocalIndex, Instruction ilStart)
|
private static void GenerateDotNetRPCSerializerType(ref DotnetContext context, TypeDefinition type, bool serialize, int localIndex, TypeReference valueType, ILProcessor il, TypeDefinition networkStreamType, int streamLocalIndex, Instruction ilStart)
|
||||||
{
|
{
|
||||||
ModuleDefinition module = type.Module;
|
ModuleDefinition module = type.Module;
|
||||||
TypeDefinition valueTypeDef = valueType.Resolve();
|
TypeDefinition valueTypeDef = valueType.Resolve();
|
||||||
|
|
||||||
|
// Ensure to have valid serialization already generated for that value type
|
||||||
|
GenerateTypeSerialization(ref context, valueTypeDef);
|
||||||
|
|
||||||
if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
||||||
{
|
{
|
||||||
// Call NetworkStream method to write/read data
|
// Call NetworkStream method to write/read data
|
||||||
@@ -1127,7 +1207,7 @@ namespace Flax.Build.Plugins
|
|||||||
module.GetType("System.Guid", out var guidType);
|
module.GetType("System.Guid", out var guidType);
|
||||||
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
||||||
if (serialize)
|
if (serialize)
|
||||||
{
|
{
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Nop));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Nop));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, localIndex));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, localIndex));
|
||||||
@@ -1209,39 +1289,43 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
// Unknown type
|
// Unknown type
|
||||||
Log.Error($"Not supported type '{valueType.FullName}' for RPC parameter in {type.FullName}.");
|
Log.Error($"Not supported type '{valueType.FullName}' for RPC parameter in {type.FullName}.");
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateDotNetRPCBody(TypeDefinition type, MethodDefinition method, CustomAttribute attribute, ref bool failed, TypeReference networkStreamType, List<MethodRPC> methodRPCs)
|
private static void GenerateDotNetRPCBody(ref DotnetContext context, TypeDefinition type, MethodDefinition method, CustomAttribute attribute, TypeReference networkStreamType)
|
||||||
{
|
{
|
||||||
// Validate RPC usage
|
// Validate RPC usage
|
||||||
if (method.IsAbstract)
|
if (method.IsAbstract)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Not supported abstract RPC method '{method.FullName}'.", method);
|
MonoCecil.CompilationError($"Not supported abstract RPC method '{method.FullName}'.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.IsVirtual)
|
if (method.IsVirtual)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Not supported virtual RPC method '{method.FullName}'.", method);
|
MonoCecil.CompilationError($"Not supported virtual RPC method '{method.FullName}'.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleDefinition module = type.Module;
|
ModuleDefinition module = type.Module;
|
||||||
var voidType = module.TypeSystem.Void;
|
var voidType = module.TypeSystem.Void;
|
||||||
if (method.ReturnType != voidType)
|
if (method.ReturnType != voidType)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Not supported non-void RPC method '{method.FullName}'.", method);
|
MonoCecil.CompilationError($"Not supported non-void RPC method '{method.FullName}'.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.IsStatic)
|
if (method.IsStatic)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Not supported static RPC method '{method.FullName}'.", method);
|
MonoCecil.CompilationError($"Not supported static RPC method '{method.FullName}'.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var methodRPC = new MethodRPC();
|
var methodRPC = new MethodRPC();
|
||||||
methodRPC.Type = type;
|
methodRPC.Type = type;
|
||||||
methodRPC.Method = method;
|
methodRPC.Method = method;
|
||||||
@@ -1252,21 +1336,24 @@ namespace Flax.Build.Plugins
|
|||||||
methodRPC.IsClient = (bool)attribute.ConstructorArguments[1].Value;
|
methodRPC.IsClient = (bool)attribute.ConstructorArguments[1].Value;
|
||||||
methodRPC.Channel = (int)attribute.ConstructorArguments[2].Value;
|
methodRPC.Channel = (int)attribute.ConstructorArguments[2].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", methodRPC.IsServer);
|
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", methodRPC.IsServer);
|
||||||
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", methodRPC.IsClient);
|
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", methodRPC.IsClient);
|
||||||
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", methodRPC.Channel);
|
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", methodRPC.Channel);
|
||||||
if (methodRPC.IsServer && methodRPC.IsClient)
|
if (methodRPC.IsServer && methodRPC.IsClient)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.", method);
|
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!methodRPC.IsServer && !methodRPC.IsClient)
|
if (!methodRPC.IsServer && !methodRPC.IsClient)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.", method);
|
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.GetType("System.IntPtr", out var intPtrType);
|
module.GetType("System.IntPtr", out var intPtrType);
|
||||||
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
||||||
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
|
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
|
||||||
@@ -1294,7 +1381,7 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
|
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
|
||||||
il.Emit(OpCodes.Castclass, networkStream);
|
il.Emit(OpCodes.Castclass, networkStream);
|
||||||
il.Emit(OpCodes.Stloc_1);
|
il.Emit(OpCodes.Stloc_1);
|
||||||
|
|
||||||
// Add locals for each RPC parameter
|
// Add locals for each RPC parameter
|
||||||
var argsStart = il.Body.Variables.Count;
|
var argsStart = il.Body.Variables.Count;
|
||||||
for (int i = 0; i < method.Parameters.Count; i++)
|
for (int i = 0; i < method.Parameters.Count; i++)
|
||||||
@@ -1303,9 +1390,10 @@ namespace Flax.Build.Plugins
|
|||||||
if (parameter.IsOut)
|
if (parameter.IsOut)
|
||||||
{
|
{
|
||||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} parameter {parameter.Name} cannot be 'out'.", method);
|
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} parameter {parameter.Name} cannot be 'out'.", method);
|
||||||
failed = true;
|
context.Failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parameterType = parameter.ParameterType;
|
var parameterType = parameter.ParameterType;
|
||||||
il.Body.Variables.Add(new VariableDefinition(parameterType));
|
il.Body.Variables.Add(new VariableDefinition(parameterType));
|
||||||
}
|
}
|
||||||
@@ -1315,7 +1403,7 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
var parameter = method.Parameters[i];
|
var parameter = method.Parameters[i];
|
||||||
var parameterType = parameter.ParameterType;
|
var parameterType = parameter.ParameterType;
|
||||||
GenerateDotNetRPCSerializerType(type, false, ref failed, argsStart + i, parameterType, il, networkStream.Resolve(), 1, null);
|
GenerateDotNetRPCSerializerType(ref context, type, false, argsStart + i, parameterType, il, networkStream.Resolve(), 1, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call RPC method body
|
// Call RPC method body
|
||||||
@@ -1324,8 +1412,9 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
il.Emit(OpCodes.Ldloc, argsStart + i);
|
il.Emit(OpCodes.Ldloc, argsStart + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
il.Emit(OpCodes.Callvirt, method);
|
il.Emit(OpCodes.Callvirt, method);
|
||||||
|
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Ret);
|
il.Emit(OpCodes.Ret);
|
||||||
type.Methods.Add(m);
|
type.Methods.Add(m);
|
||||||
@@ -1371,10 +1460,10 @@ namespace Flax.Build.Plugins
|
|||||||
// ||
|
// ||
|
||||||
il.InsertBefore(ilStart, jumpIf2Start);
|
il.InsertBefore(ilStart, jumpIf2Start);
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 1));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 1));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Brfalse_S, jumpBodyStart));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Brfalse, jumpBodyStart));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_2));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_2));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, jumpBodyStart));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Beq, jumpBodyStart));
|
||||||
// {
|
// {
|
||||||
il.InsertBefore(ilStart, jumpIfBodyStart);
|
il.InsertBefore(ilStart, jumpIfBodyStart);
|
||||||
|
|
||||||
@@ -1391,7 +1480,7 @@ namespace Flax.Build.Plugins
|
|||||||
{
|
{
|
||||||
var parameter = method.Parameters[i];
|
var parameter = method.Parameters[i];
|
||||||
var parameterType = parameter.ParameterType;
|
var parameterType = parameter.ParameterType;
|
||||||
GenerateDotNetRPCSerializerType(type, true, ref failed, i + 1, parameterType, il, networkStream.Resolve(), streamLocalIndex, ilStart);
|
GenerateDotNetRPCSerializerType(ref context, type, true, i + 1, parameterType, il, networkStream.Resolve(), streamLocalIndex, ilStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkReplicator.EndInvokeRPC(this, typeof(<type>), "<name>", stream);
|
// NetworkReplicator.EndInvokeRPC(this, typeof(<type>), "<name>", stream);
|
||||||
@@ -1418,7 +1507,6 @@ namespace Flax.Build.Plugins
|
|||||||
il.InsertBefore(ilStart, tmp);
|
il.InsertBefore(ilStart, tmp);
|
||||||
//il.InsertBefore(ilStart, il.Create(OpCodes.Ret));
|
//il.InsertBefore(ilStart, il.Create(OpCodes.Ret));
|
||||||
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd));
|
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (client && networkMode == NetworkManagerMode.Server) return;
|
// if (client && networkMode == NetworkManagerMode.Server) return;
|
||||||
@@ -1439,7 +1527,7 @@ namespace Flax.Build.Plugins
|
|||||||
il.InsertBefore(ilStart, jumpBodyStart);
|
il.InsertBefore(ilStart, jumpBodyStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
methodRPCs.Add(methodRPC);
|
context.MethodRPCs.Add(methodRPC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user