Merge branch '1.5' into dotnet7
This commit is contained in:
@@ -156,88 +156,126 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
pickerData.IsEditing = false;
|
||||
}
|
||||
|
||||
|
||||
private static void OnAddButtonClicked(Tree tree, TreeNode parentNode, PickerData pickerData)
|
||||
private static void OnAddTagButtonClicked(string input, Tree tree, TextBox textBox, DropPanel dropPanel, PickerData pickerData)
|
||||
{
|
||||
// Create new node
|
||||
var nodeIndent = 16.0f;
|
||||
var indentation = 0;
|
||||
var parentTag = string.Empty;
|
||||
if (parentNode.CustomArrowRect.HasValue)
|
||||
{
|
||||
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
|
||||
var parentTagValue = (Tag)parentNode.Tag;
|
||||
parentTag = parentTagValue.ToString();
|
||||
}
|
||||
var node = new TreeNodeWithAddons
|
||||
{
|
||||
ChildrenIndent = nodeIndent,
|
||||
CullChildren = false,
|
||||
ClipChildren = false,
|
||||
TextMargin = new Margin(22.0f, 2.0f, 2.0f, 2.0f),
|
||||
CustomArrowRect = new Rectangle(18 + indentation * nodeIndent, 2, 12, 12),
|
||||
BackgroundColorSelected = Color.Transparent,
|
||||
BackgroundColorHighlighted = parentNode.BackgroundColorHighlighted,
|
||||
};
|
||||
var checkbox = new CheckBox(32.0f + indentation * nodeIndent, 0)
|
||||
{
|
||||
Height = 16.0f,
|
||||
IsScrollable = false,
|
||||
Parent = node,
|
||||
};
|
||||
node.Addons.Add(checkbox);
|
||||
checkbox.StateChanged += c => OnCheckboxEdited(node, c, pickerData);
|
||||
var addButton = new Button(tree.Width - 16, 0, 14, 14)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add subtag within this tag namespace",
|
||||
IsScrollable = false,
|
||||
BorderColor = Color.Transparent,
|
||||
BackgroundColor = Color.Transparent,
|
||||
Parent = node,
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
};
|
||||
node.Addons.Add(addButton);
|
||||
addButton.ButtonClicked += button => OnAddButtonClicked(tree, node, pickerData);
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return;
|
||||
|
||||
// Link
|
||||
node.Parent = parentNode;
|
||||
node.IndexInParent = 0;
|
||||
parentNode.Expand(true);
|
||||
((Panel)tree.Parent.Parent).ScrollViewTo(node);
|
||||
// Ensure that name is unique
|
||||
if (Tags.List.Contains(input))
|
||||
return;
|
||||
|
||||
// Start renaming the tag
|
||||
var prefix = parentTag.Length != 0 ? parentTag + '.' : string.Empty;
|
||||
var renameArea = node.HeaderRect;
|
||||
if (renameArea.Location.X < nodeIndent)
|
||||
{
|
||||
// Fix root node's child renaming
|
||||
renameArea.Location.X += nodeIndent;
|
||||
renameArea.Size.X -= nodeIndent;
|
||||
}
|
||||
var dialog = RenamePopup.Show(node, renameArea, prefix, false);
|
||||
var cursor = dialog.InputField.TextLength;
|
||||
dialog.InputField.SelectionRange = new TextRange(cursor, cursor);
|
||||
dialog.Validate = (popup, value) =>
|
||||
{
|
||||
// Ensure that name is unique
|
||||
if (Tags.List.Contains(value))
|
||||
return false;
|
||||
var subInputs = input.Split('.');
|
||||
string tagString = string.Empty;
|
||||
string lastTagString = string.Empty;
|
||||
|
||||
// Ensure user entered direct subtag of the parent node
|
||||
if (value.StartsWith(popup.InitialValue))
|
||||
TreeNode parentNode = tree.GetChild<TreeNode>(); // Start at root
|
||||
int parentCount = 0;
|
||||
for (int i = 0; i < subInputs.Length; i++)
|
||||
{
|
||||
if (string.IsNullOrEmpty(subInputs[i]))
|
||||
{
|
||||
var name = value.Substring(popup.InitialValue.Length);
|
||||
if (name.Length > 0 && name.IndexOf('.') == -1)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
dialog.Renamed += popup =>
|
||||
{
|
||||
|
||||
// Check all entered subtags and create any that dont exist
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
tagString += j == 0 ? subInputs[j] : "." + subInputs[j];
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(tagString))
|
||||
{
|
||||
tagString = string.Empty;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Tags.List.Contains(tagString))
|
||||
{
|
||||
// Find next parent node
|
||||
foreach (var child in parentNode.Children)
|
||||
{
|
||||
if (!(child is TreeNode childNode))
|
||||
continue;
|
||||
|
||||
var tagValue = (Tag)childNode.Tag;
|
||||
if (!string.Equals(tagValue.ToString(), tagString))
|
||||
continue;
|
||||
|
||||
parentNode = childNode;
|
||||
parentCount += 1;
|
||||
}
|
||||
lastTagString = tagString;
|
||||
tagString = string.Empty;
|
||||
continue;
|
||||
}
|
||||
else if (subInputs.Length > 1)
|
||||
{
|
||||
// Find next parent node
|
||||
foreach (var child in parentNode.Children)
|
||||
{
|
||||
if (!(child is TreeNode childNode))
|
||||
continue;
|
||||
|
||||
var tagValue = (Tag)childNode.Tag;
|
||||
if (!string.Equals(tagValue.ToString(), lastTagString))
|
||||
continue;
|
||||
|
||||
parentNode = childNode;
|
||||
parentCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new node
|
||||
var nodeIndent = 16.0f;
|
||||
var indentation = 0;
|
||||
var parentTag = string.Empty;
|
||||
if (parentNode.CustomArrowRect.HasValue)
|
||||
{
|
||||
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
|
||||
var parentTagValue = (Tag)parentNode.Tag;
|
||||
parentTag = parentTagValue.ToString();
|
||||
}
|
||||
var node = new TreeNodeWithAddons
|
||||
{
|
||||
ChildrenIndent = nodeIndent,
|
||||
CullChildren = false,
|
||||
ClipChildren = false,
|
||||
TextMargin = new Margin(22.0f, 2.0f, 2.0f, 2.0f),
|
||||
CustomArrowRect = new Rectangle(18 + indentation * nodeIndent, 2, 12, 12),
|
||||
BackgroundColorSelected = Color.Transparent,
|
||||
BackgroundColorHighlighted = parentNode.BackgroundColorHighlighted,
|
||||
};
|
||||
var checkbox = new CheckBox(32.0f + indentation * nodeIndent, 0)
|
||||
{
|
||||
Height = 16.0f,
|
||||
IsScrollable = false,
|
||||
Parent = node,
|
||||
};
|
||||
node.Addons.Add(checkbox);
|
||||
checkbox.StateChanged += c => OnCheckboxEdited(node, c, pickerData);
|
||||
var addButton = new Button(tree.Width - 16, 0, 14, 14)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add subtag within this tag namespace",
|
||||
IsScrollable = false,
|
||||
BorderColor = Color.Transparent,
|
||||
BackgroundColor = Color.Transparent,
|
||||
Parent = node,
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
};
|
||||
node.Addons.Add(addButton);
|
||||
addButton.ButtonClicked += button => OnAddSubTagButtonClicked(((Tag)node.Tag).ToString(), textBox, dropPanel);
|
||||
|
||||
// Link
|
||||
node.Parent = parentNode;
|
||||
node.IndexInParent = 0;
|
||||
parentNode.Expand(true);
|
||||
((Panel)tree.Parent.Parent).ScrollViewTo(node);
|
||||
|
||||
// Get tag name
|
||||
var tagName = popup.Text;
|
||||
var tagShortName = tagName.Substring(popup.InitialValue.Length);
|
||||
var tagName = tagString;
|
||||
var tagShortName = tagName.Substring(parentCount == 0 ? lastTagString.Length : lastTagString.Length + 1);
|
||||
if (tagShortName.Length == 0)
|
||||
return;
|
||||
|
||||
@@ -256,24 +294,134 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Update asset
|
||||
var settingsObj = (LayersAndTagsSettings)settingsAsset.Instance;
|
||||
settingsObj.Tags.Add(tagName);
|
||||
settingsObj.Tags.Sort();
|
||||
settingsAsset.SetInstance(settingsObj);
|
||||
|
||||
// Reload editor window to reflect new tag
|
||||
assetWindow?.RefreshAsset();
|
||||
}
|
||||
};
|
||||
dialog.Closed += popup =>
|
||||
|
||||
lastTagString = tagString;
|
||||
tagString = string.Empty;
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
}
|
||||
|
||||
private static void OnAddSubTagButtonClicked(string parentTag, TextBox textBox, DropPanel dropPanel)
|
||||
{
|
||||
if (textBox == null || dropPanel == null || string.IsNullOrEmpty(parentTag))
|
||||
{
|
||||
// Remove temporary node if renaming was canceled
|
||||
if (popup.InitialValue == popup.Text || popup.Text.Length == 0)
|
||||
node.Dispose();
|
||||
};
|
||||
return;
|
||||
}
|
||||
dropPanel.Open();
|
||||
textBox.Text = parentTag + ".";
|
||||
textBox.Focus();
|
||||
textBox.SelectionRange = new TextRange(textBox.Text.Length, textBox.Text.Length);
|
||||
}
|
||||
|
||||
internal static ContextMenuBase CreatePicker(Tag value, Tag[] values, PickerData pickerData)
|
||||
{
|
||||
// Initialize search popup
|
||||
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 20.0f);
|
||||
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 40.0f);
|
||||
|
||||
// Add tag drop panel
|
||||
var addTagDropPanel = new DropPanel
|
||||
{
|
||||
HeaderText = "Add Tag",
|
||||
EnableDropDownIcon = true,
|
||||
ArrowImageOpened = new SpriteBrush(FlaxEngine.GUI.Style.Current.ArrowDown),
|
||||
ArrowImageClosed = new SpriteBrush(FlaxEngine.GUI.Style.Current.ArrowRight),
|
||||
Parent = menu,
|
||||
HeaderTextMargin = new Margin(2.0f),
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Bounds = new Rectangle(2, 2, menu.Width - 4, 30),
|
||||
IsClosed = true,
|
||||
ItemsMargin = new Margin(2.0f),
|
||||
CloseAnimationTime = 0,
|
||||
};
|
||||
var tagNamePanel = new HorizontalPanel
|
||||
{
|
||||
Parent = addTagDropPanel,
|
||||
AutoSize = false,
|
||||
Bounds = new Rectangle(0, 0, addTagDropPanel.Width, 20.0f),
|
||||
};
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Size = new Float2(addTagDropPanel.Width / 4, 0),
|
||||
Parent = tagNamePanel,
|
||||
Text = "Tag Name:",
|
||||
};
|
||||
var nameTextBox = new TextBox
|
||||
{
|
||||
IsMultiline = false,
|
||||
WatermarkText = "X.Y.Z",
|
||||
Size = new Float2(addTagDropPanel.Width * 0.7f, 0),
|
||||
Parent = tagNamePanel,
|
||||
EndEditOnClick = false,
|
||||
};
|
||||
|
||||
bool uniqueText = true;
|
||||
nameTextBox.TextChanged += () =>
|
||||
{
|
||||
// Ensure that name is unique
|
||||
if (Tags.List.Contains(nameTextBox.Text))
|
||||
uniqueText = false;
|
||||
else
|
||||
uniqueText = true;
|
||||
|
||||
var currentStyle = FlaxEngine.GUI.Style.Current;
|
||||
if (uniqueText)
|
||||
{
|
||||
nameTextBox.BorderColor = Color.Transparent;
|
||||
nameTextBox.BorderSelectedColor = currentStyle.BackgroundSelected;
|
||||
}
|
||||
else
|
||||
{
|
||||
var color = new Color(1.0f, 0.0f, 0.02745f, 1.0f);
|
||||
nameTextBox.BorderColor = Color.Lerp(color, currentStyle.TextBoxBackground, 0.6f);
|
||||
nameTextBox.BorderSelectedColor = color;
|
||||
}
|
||||
};
|
||||
|
||||
nameTextBox.EditEnd += () =>
|
||||
{
|
||||
if (!uniqueText)
|
||||
return;
|
||||
|
||||
if (addTagDropPanel.IsClosed)
|
||||
{
|
||||
Debug.Log("Hit");
|
||||
nameTextBox.BorderColor = Color.Transparent;
|
||||
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||
return;
|
||||
}
|
||||
|
||||
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
|
||||
};
|
||||
|
||||
var addButtonPanel = new HorizontalPanel
|
||||
{
|
||||
Parent = addTagDropPanel,
|
||||
AutoSize = false,
|
||||
Bounds = new Rectangle(0, 0, addTagDropPanel.Width, 30.0f),
|
||||
};
|
||||
var buttonAddTag = new Button
|
||||
{
|
||||
Parent = addButtonPanel,
|
||||
Size = new Float2(100, 20),
|
||||
Text = "Add Tag",
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
};
|
||||
buttonAddTag.Clicked += () =>
|
||||
{
|
||||
if (!uniqueText)
|
||||
return;
|
||||
|
||||
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
|
||||
};
|
||||
|
||||
// Used for how far everything should drop when the drop down panel is opened
|
||||
var dropPanelOpenHeight = tagNamePanel.Height + addButtonPanel.Height + 4;
|
||||
|
||||
// Create tree with tags hierarchy
|
||||
tree.Margin = new Margin(-16.0f, 0.0f, -16.0f, -0.0f); // Hide root node
|
||||
@@ -306,7 +454,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
tagShortName = tagName.Substring(lastDotIndex + 1);
|
||||
tagParentName = tagName.Substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
|
||||
// Create node
|
||||
var node = new TreeNodeWithAddons
|
||||
{
|
||||
@@ -339,7 +487,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
};
|
||||
node.Addons.Add(addButton);
|
||||
addButton.ButtonClicked += button => OnAddButtonClicked(tree, node, pickerData);
|
||||
addButton.ButtonClicked += button => OnAddSubTagButtonClicked(((Tag)node.Tag).ToString(), nameTextBox, addTagDropPanel);
|
||||
|
||||
// Link to parent
|
||||
{
|
||||
@@ -360,10 +508,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
Margin = new Margin(1.0f),
|
||||
AutoSize = false,
|
||||
Bounds = new Rectangle(0, 0, menu.Width, 20.0f),
|
||||
Bounds = new Rectangle(0, 0, menu.Width - 4, 20.0f),
|
||||
Parent = menu,
|
||||
};
|
||||
var buttonsSize = new Float2((menu.Width - buttonsPanel.Margin.Width) / 4.0f - buttonsPanel.Spacing, 18.0f);
|
||||
buttonsPanel.Y += addTagDropPanel.HeaderHeight + 4;
|
||||
buttonsPanel.X += 2;
|
||||
|
||||
var buttonsSize = new Float2((menu.Width - 2 - buttonsPanel.Margin.Width) / 3.0f - buttonsPanel.Spacing, 18.0f);
|
||||
var buttonExpandAll = new Button
|
||||
{
|
||||
Size = buttonsSize,
|
||||
@@ -382,20 +533,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
root.CollapseAll(true);
|
||||
root.Expand(true);
|
||||
};
|
||||
var buttonAddTag = new Button
|
||||
var buttonClearAll = new Button
|
||||
{
|
||||
Size = buttonsSize,
|
||||
Parent = buttonsPanel,
|
||||
Text = "Add Tag",
|
||||
Text = "Clear all",
|
||||
};
|
||||
buttonAddTag.Clicked += () => OnAddButtonClicked(tree, root, pickerData);
|
||||
var buttonReset = new Button
|
||||
{
|
||||
Size = buttonsSize,
|
||||
Parent = buttonsPanel,
|
||||
Text = "Reset",
|
||||
};
|
||||
buttonReset.Clicked += () =>
|
||||
buttonClearAll.Clicked += () =>
|
||||
{
|
||||
pickerData.IsEditing = true;
|
||||
UncheckAll(root);
|
||||
@@ -404,6 +548,48 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
pickerData.SetValues?.Invoke(null);
|
||||
};
|
||||
|
||||
// Move menu children to location when drop panel is opened and closed
|
||||
addTagDropPanel.IsClosedChanged += panel =>
|
||||
{
|
||||
if (panel.IsClosed)
|
||||
{
|
||||
// Resize/ Move UI to fit space with drop down
|
||||
foreach (var child in menu.Children)
|
||||
{
|
||||
if (child == panel)
|
||||
continue;
|
||||
|
||||
// Expand panels so scrollbars work
|
||||
if (child is Panel)
|
||||
{
|
||||
child.Bounds = new Rectangle(child.Bounds.X, child.Bounds.Y - dropPanelOpenHeight, child.Width, child.Height + dropPanelOpenHeight);
|
||||
continue;
|
||||
}
|
||||
child.Y -= dropPanelOpenHeight;
|
||||
nameTextBox.Text = string.Empty;
|
||||
nameTextBox.Defocus();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize/ Move UI to fit space with drop down
|
||||
foreach (var child in menu.Children)
|
||||
{
|
||||
if (child == panel)
|
||||
continue;
|
||||
|
||||
// Shrink panels so scrollbars work
|
||||
if (child is Panel)
|
||||
{
|
||||
child.Bounds = new Rectangle(child.Bounds.X, child.Bounds.Y + dropPanelOpenHeight, child.Width, child.Height - dropPanelOpenHeight);
|
||||
continue;
|
||||
}
|
||||
child.Y += dropPanelOpenHeight;
|
||||
nameTextBox.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Setup search filter
|
||||
searchBox.TextChanged += delegate
|
||||
{
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace FlaxEditor.Windows
|
||||
"Lukáš Jech",
|
||||
"Jean-Baptiste Perrier",
|
||||
"Chandler Cox",
|
||||
"Ari Vuollet",
|
||||
});
|
||||
authors.Sort();
|
||||
var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70)
|
||||
|
||||
@@ -313,7 +313,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
public void RefreshAsset()
|
||||
{
|
||||
_isWaitingForLoaded = true;
|
||||
if (_asset == null || _asset.WaitForLoaded())
|
||||
return;
|
||||
|
||||
OnAssetLoaded();
|
||||
MarkAsEdited();
|
||||
Save();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -816,15 +816,23 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
|
||||
// Selected UI controls outline
|
||||
bool drawAnySelectedControl = false;
|
||||
for (var i = 0; i < Editor.Instance.SceneEditing.Selection.Count; i++)
|
||||
{
|
||||
if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
||||
{
|
||||
if (!drawAnySelectedControl)
|
||||
{
|
||||
drawAnySelectedControl = true;
|
||||
Render2D.PushTransform(ref _viewport._cachedTransform);
|
||||
}
|
||||
var control = controlActor.Control;
|
||||
var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Float2.Zero), control.PointToParent(_viewport, control.Size));
|
||||
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
||||
}
|
||||
}
|
||||
if (drawAnySelectedControl)
|
||||
Render2D.PopTransform();
|
||||
|
||||
// Play mode hints and overlay
|
||||
if (Editor.StateMachine.IsPlayMode)
|
||||
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
/// <param name="data">Data to copy.</param>
|
||||
void Copy(const DataContainer& data)
|
||||
{
|
||||
if (data.IsValid())
|
||||
if (data.Length() != 0)
|
||||
Copy(data.Get(), data.Length());
|
||||
else
|
||||
Release();
|
||||
@@ -243,7 +243,7 @@ public:
|
||||
/// <param name="data">Data to copy.</param>
|
||||
void Copy(const Span<T>& data)
|
||||
{
|
||||
if (data.IsValid())
|
||||
if (data.Length() != 0)
|
||||
Copy(data.Get(), data.Length());
|
||||
else
|
||||
Release();
|
||||
|
||||
@@ -897,6 +897,13 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
|
||||
rpc.Info = *info;
|
||||
const Span<byte> argsData(argsStream->GetBuffer(), argsStream->GetPosition());
|
||||
rpc.ArgsData.Copy(argsData);
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it == Objects.End())
|
||||
{
|
||||
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", type.ToString(), String(name), obj->GetID());
|
||||
}
|
||||
#endif
|
||||
ObjectsLock.Unlock();
|
||||
}
|
||||
|
||||
@@ -980,6 +987,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
CachedWriteStream = New<NetworkStream>();
|
||||
const bool isClient = NetworkManager::IsClient();
|
||||
const bool isServer = NetworkManager::IsServer();
|
||||
const bool isHost = NetworkManager::IsHost();
|
||||
NetworkStream* stream = CachedWriteStream;
|
||||
NetworkPeer* peer = NetworkManager::Peer;
|
||||
|
||||
@@ -1279,10 +1287,10 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
// Client -> Server
|
||||
peer->EndSendMessage(channel, msg);
|
||||
}
|
||||
else if (e.Info.Client && isServer)
|
||||
else if (e.Info.Client && (isServer || isHost))
|
||||
{
|
||||
// Server -> Client(s)
|
||||
BuildCachedTargets(item);
|
||||
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, NetworkManager::LocalClientId);
|
||||
peer->EndSendMessage(channel, msg, CachedTargets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,17 +379,11 @@ namespace Flax.Build
|
||||
// Reference module output binary
|
||||
fileReferences.Add(Path.Combine(outputPath, dependencyModule.BinaryModuleName + ".CSharp.dll"));
|
||||
}
|
||||
foreach (var e in buildData.ReferenceBuilds)
|
||||
var referencedBuild = buildData.FinReferenceBuildModule(dependencyModule.BinaryModuleName);
|
||||
if (referencedBuild != null && !string.IsNullOrEmpty(referencedBuild.ManagedPath))
|
||||
{
|
||||
foreach (var q in e.Value.BuildInfo.BinaryModules)
|
||||
{
|
||||
if (q.Name == dependencyModule.BinaryModuleName && !string.IsNullOrEmpty(q.ManagedPath))
|
||||
{
|
||||
// Reference binary module build build for referenced target
|
||||
fileReferences.Add(q.ManagedPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Reference binary module build build for referenced target
|
||||
fileReferences.Add(referencedBuild.ManagedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,13 +805,13 @@ namespace Flax.Build.Plugins
|
||||
{
|
||||
if (property.GetMethod == null)
|
||||
{
|
||||
Log.Error($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.");
|
||||
MonoCecil.CompilationError($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
if (property.SetMethod == null)
|
||||
{
|
||||
Log.Error($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.");
|
||||
MonoCecil.CompilationError($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
@@ -1090,7 +1090,12 @@ namespace Flax.Build.Plugins
|
||||
else
|
||||
{
|
||||
// Unknown type
|
||||
Log.Error($"Not supported type '{valueType.FullName}' on {(field?.Name ?? property.Name)} in {type.FullName} for automatic replication.");
|
||||
if (property != null)
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {property.Name} in {type.FullName} for automatic replication.", property);
|
||||
else if (field != null)
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {field.Name} in {type.FullName} for automatic replication.", field.Resolve());
|
||||
else
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' for automatic replication.");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
@@ -1212,13 +1217,13 @@ namespace Flax.Build.Plugins
|
||||
// Validate RPC usage
|
||||
if (method.IsAbstract)
|
||||
{
|
||||
Log.Error($"Not supported abstract RPC method '{method.FullName}'.");
|
||||
MonoCecil.CompilationError($"Not supported abstract RPC method '{method.FullName}'.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
if (method.IsVirtual)
|
||||
{
|
||||
Log.Error($"Not supported virtual RPC method '{method.FullName}'.");
|
||||
MonoCecil.CompilationError($"Not supported virtual RPC method '{method.FullName}'.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
@@ -1226,13 +1231,13 @@ namespace Flax.Build.Plugins
|
||||
var voidType = module.TypeSystem.Void;
|
||||
if (method.ReturnType != voidType)
|
||||
{
|
||||
Log.Error($"Not supported non-void RPC method '{method.FullName}'.");
|
||||
MonoCecil.CompilationError($"Not supported non-void RPC method '{method.FullName}'.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
if (method.IsStatic)
|
||||
{
|
||||
Log.Error($"Not supported static RPC method '{method.FullName}'.");
|
||||
MonoCecil.CompilationError($"Not supported static RPC method '{method.FullName}'.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
@@ -1241,19 +1246,25 @@ namespace Flax.Build.Plugins
|
||||
methodRPC.Method = method;
|
||||
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", false);
|
||||
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", false);
|
||||
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", 4); // int as NetworkChannelType (default is ReliableOrdered=4)
|
||||
if (attribute.HasConstructorArguments && attribute.ConstructorArguments.Count >= 3)
|
||||
{
|
||||
methodRPC.IsServer = (bool)attribute.ConstructorArguments[0].Value;
|
||||
methodRPC.IsClient = (bool)attribute.ConstructorArguments[1].Value;
|
||||
methodRPC.Channel = (int)attribute.ConstructorArguments[2].Value;
|
||||
}
|
||||
if (methodRPC.IsServer && methodRPC.IsClient)
|
||||
{
|
||||
Log.Error($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.");
|
||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
if (!methodRPC.IsServer && !methodRPC.IsClient)
|
||||
{
|
||||
Log.Error($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.");
|
||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", 4); // int as NetworkChannelType (default is ReliableOrdered=4)
|
||||
module.GetType("System.IntPtr", out var intPtrType);
|
||||
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
|
||||
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
|
||||
@@ -1289,7 +1300,7 @@ namespace Flax.Build.Plugins
|
||||
var parameter = method.Parameters[i];
|
||||
if (parameter.IsOut)
|
||||
{
|
||||
Log.Error($"Network RPC {method.Name} in {type.FullName} parameter {parameter.Name} cannot be 'out'.");
|
||||
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} parameter {parameter.Name} cannot be 'out'.", method);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,58 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
internal static class MonoCecil
|
||||
{
|
||||
public static void CompilationError(string message)
|
||||
{
|
||||
Log.Error(message);
|
||||
}
|
||||
|
||||
public static void CompilationError(string message, MethodDefinition method)
|
||||
{
|
||||
if (method != null && method.DebugInformation.HasSequencePoints)
|
||||
{
|
||||
var sp = method.DebugInformation.SequencePoints[0];
|
||||
message = $"{sp.Document.Url}({sp.StartLine},{sp.StartColumn},{sp.EndLine},{sp.EndColumn}): error: {message}";
|
||||
}
|
||||
Log.Error(message);
|
||||
}
|
||||
|
||||
public static void CompilationError(string message, PropertyDefinition property)
|
||||
{
|
||||
if (property != null)
|
||||
{
|
||||
if (property.GetMethod != null)
|
||||
{
|
||||
CompilationError(message, property.GetMethod);
|
||||
return;
|
||||
}
|
||||
else if (property.SetMethod != null)
|
||||
{
|
||||
CompilationError(message, property.SetMethod);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Log.Error(message);
|
||||
}
|
||||
|
||||
public static void CompilationError(string message, FieldDefinition field)
|
||||
{
|
||||
if (field != null && field.DeclaringType != null)
|
||||
{
|
||||
// Just include potential filename
|
||||
var methods = field.DeclaringType.Methods;
|
||||
if (methods != null && methods.Count != 0)
|
||||
{
|
||||
var method = methods[0];
|
||||
if (method != null && method.DebugInformation.HasSequencePoints)
|
||||
{
|
||||
var sp = method.DebugInformation.SequencePoints[0];
|
||||
message = $"{sp.Document.Url}({0},{0},{0},{0}): error: {message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.Error(message);
|
||||
}
|
||||
|
||||
public static bool HasAttribute(this ICustomAttributeProvider type, string fullName)
|
||||
{
|
||||
return type.CustomAttributes.Any(x => x.AttributeType.FullName == fullName);
|
||||
|
||||
Reference in New Issue
Block a user