Merge branch 'FlaxEngine:master' into color-picker

This commit is contained in:
Menotdan
2023-05-17 21:12:37 -04:00
committed by GitHub
11 changed files with 168 additions and 54 deletions

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using FlaxEditor.CustomEditors.Dedicated;
using FlaxEditor.CustomEditors.Editors; using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.Scripting; using FlaxEditor.Scripting;
using FlaxEngine; using FlaxEngine;
@@ -118,6 +119,8 @@ namespace FlaxEditor.CustomEditors
if (customEditorType != null) if (customEditorType != null)
return (CustomEditor)Activator.CreateInstance(customEditorType); return (CustomEditor)Activator.CreateInstance(customEditorType);
} }
if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType))
return new ScriptingObjectEditor();
// The most generic editor // The most generic editor
return new GenericEditor(); return new GenericEditor();

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// </summary> /// </summary>
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" /> /// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
[CustomEditor(typeof(Actor)), DefaultEditor] [CustomEditor(typeof(Actor)), DefaultEditor]
public class ActorEditor : GenericEditor public class ActorEditor : ScriptingObjectEditor
{ {
private Guid _linkedPrefabId; private Guid _linkedPrefabId;

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEditor.CustomEditors.Editors;
using FlaxEngine.Networking;
namespace FlaxEditor.CustomEditors.Dedicated
{
/// <summary>
/// Custom editor for <see cref="FlaxEngine.Object"/>.
/// </summary>
public class ScriptingObjectEditor : GenericEditor
{
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
{
// Network objects debugging
var obj = Values[0] as FlaxEngine.Object;
if (Editor.IsPlayMode && NetworkManager.IsConnected && NetworkReplicator.HasObject(obj))
{
var group = layout.Group("Network");
group.Panel.Open();
group.Label("Role", Utilities.Utils.GetPropertyNameUI(NetworkReplicator.GetObjectRole(obj).ToString()));
group.Label("Owner Client Id", NetworkReplicator.GetObjectOwnerClientId(obj).ToString());
}
base.Initialize(layout);
}
}
}

View File

@@ -3,7 +3,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Actions; using FlaxEditor.Actions;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.GUI; using FlaxEditor.GUI;

View File

@@ -78,7 +78,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// <seealso cref="FlaxEditor.CustomEditors.Editors.Float3Editor" /> /// <seealso cref="FlaxEditor.CustomEditors.Editors.Float3Editor" />
public class ScaleEditor : Float3Editor public class ScaleEditor : Float3Editor
{ {
private Image _linkImage; private Button _linkButton;
/// <inheritdoc /> /// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout) public override void Initialize(LayoutElementsContainer layout)
@@ -87,19 +87,20 @@ namespace FlaxEditor.CustomEditors.Editors
LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked; LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked;
_linkImage = new Image // Add button with the link icon
_linkButton = new Button
{ {
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Link32),
Parent = LinkedLabel, Parent = LinkedLabel,
Width = 18, Width = 18,
Height = 18, Height = 18,
Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush(),
AnchorPreset = AnchorPresets.TopLeft, AnchorPreset = AnchorPresets.TopLeft,
TooltipText = "Scale values are linked together.",
}; };
_linkButton.Clicked += ToggleLink;
SetLinkStyle();
var x = LinkedLabel.Text.Value.Length * 7 + 5; var x = LinkedLabel.Text.Value.Length * 7 + 5;
_linkImage.LocalX += x; _linkButton.LocalX += x;
_linkImage.LocalY += 1; _linkButton.LocalY += 1;
LinkedLabel.SetupContextMenu += (label, menu, editor) => LinkedLabel.SetupContextMenu += (label, menu, editor) =>
{ {
menu.AddSeparator(); menu.AddSeparator();
@@ -127,7 +128,16 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
LinkValues = !LinkValues; LinkValues = !LinkValues;
Editor.Instance.Windows.PropertiesWin.ScaleLinked = LinkValues; Editor.Instance.Windows.PropertiesWin.ScaleLinked = LinkValues;
_linkImage.Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush(); SetLinkStyle();
}
private void SetLinkStyle()
{
var style = FlaxEngine.GUI.Style.Current;
var backgroundColor = LinkValues ? style.Foreground : style.ForegroundDisabled;
_linkButton.SetColors(backgroundColor);
_linkButton.BorderColor = _linkButton.BorderColorSelected = _linkButton.BorderColorHighlighted = Color.Transparent;
_linkButton.TooltipText = LinkValues ? "Unlinks scale components from uniform scaling" : "Links scale components for uniform scaling";
} }
} }
} }

View File

@@ -541,7 +541,7 @@ namespace FlaxEditor.Windows
{ {
ref var line = ref lines[j]; ref var line = ref lines[j];
textBlock.Range.StartIndex = startIndex + line.FirstCharIndex; textBlock.Range.StartIndex = startIndex + line.FirstCharIndex;
textBlock.Range.EndIndex = startIndex + line.LastCharIndex; textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1;
textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size); textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size);
if (textBlock.Range.Length > 0) if (textBlock.Range.Length > 0)
@@ -550,7 +550,7 @@ namespace FlaxEditor.Windows
var regexStart = line.FirstCharIndex; var regexStart = line.FirstCharIndex;
if (j == 0) if (j == 0)
regexStart += prefixLength; regexStart += prefixLength;
var regexLength = line.LastCharIndex - regexStart; var regexLength = line.LastCharIndex + 1 - regexStart;
if (regexLength > 0) if (regexLength > 0)
{ {
var match = _compileRegex.Match(entryText, regexStart, regexLength); var match = _compileRegex.Match(entryText, regexStart, regexLength);

View File

@@ -317,11 +317,12 @@ bool NetworkManager::StartHost()
LocalClient = New<NetworkClient>(LocalClientId, NetworkConnection{ 0 }); LocalClient = New<NetworkClient>(LocalClientId, NetworkConnection{ 0 });
// Auto-connect host // Auto-connect host
LocalClient->State = NetworkConnectionState::Connecting;
State = NetworkConnectionState::Connected;
StateChanged();
LocalClient->State = NetworkConnectionState::Connected; LocalClient->State = NetworkConnectionState::Connected;
ClientConnected(LocalClient); ClientConnected(LocalClient);
State = NetworkConnectionState::Connected;
StateChanged();
return false; return false;
} }

View File

@@ -745,7 +745,7 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle,
return false; return false;
} }
void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent) void NetworkReplicator::AddObject(ScriptingObject* obj, const ScriptingObject* parent)
{ {
if (!obj || NetworkManager::IsOffline()) if (!obj || NetworkManager::IsOffline())
return; return;
@@ -774,6 +774,19 @@ void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent)
item.OwnerClientId = NetworkManager::ServerClientId; // Server owns objects by default item.OwnerClientId = NetworkManager::ServerClientId; // Server owns objects by default
item.Role = NetworkManager::IsClient() ? NetworkObjectRole::Replicated : NetworkObjectRole::OwnedAuthoritative; item.Role = NetworkManager::IsClient() ? NetworkObjectRole::Replicated : NetworkObjectRole::OwnedAuthoritative;
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Add new object {}:{}, parent {}:{}", item.ToString(), obj->GetType().ToString(), item.ParentId.ToString(), parent ? parent->GetType().ToString() : String::Empty); NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Add new object {}:{}, parent {}:{}", item.ToString(), obj->GetType().ToString(), item.ParentId.ToString(), parent ? parent->GetType().ToString() : String::Empty);
for (const SpawnItem& spawnItem : SpawnQueue)
{
if (spawnItem.HasOwnership && spawnItem.HierarchicalOwnership)
{
if (IsParentOf(obj, spawnItem.Object))
{
// Inherit ownership
item.Role = spawnItem.Role;
item.OwnerClientId = spawnItem.OwnerClientId;
break;
}
}
}
Objects.Add(MoveTemp(item)); Objects.Add(MoveTemp(item));
} }
@@ -861,10 +874,27 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj)
DeleteNetworkObject(obj); DeleteNetworkObject(obj);
} }
bool NetworkReplicator::HasObject(const ScriptingObject* obj)
{
if (obj)
{
ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID());
if (it != Objects.End())
return true;
for (const SpawnItem& item : SpawnQueue)
{
if (item.Object == obj)
return true;
}
}
return false;
}
uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
{ {
uint32 id = NetworkManager::ServerClientId; uint32 id = NetworkManager::ServerClientId;
if (obj) if (obj && NetworkManager::IsConnected())
{ {
ScopeLock lock(ObjectsLock); ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID()); const auto it = Objects.Find(obj->GetID());
@@ -878,9 +908,16 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
{ {
if (item.HasOwnership) if (item.HasOwnership)
id = item.OwnerClientId; id = item.OwnerClientId;
#if USE_NETWORK_REPLICATOR_LOG
return id;
#else
break; break;
#endif
} }
} }
#if USE_NETWORK_REPLICATOR_LOG
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to get ownership of unregistered network object {} ({})", obj->GetID(), obj->GetType().ToString());
#endif
} }
} }
return id; return id;
@@ -889,7 +926,7 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj) NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj)
{ {
NetworkObjectRole role = NetworkObjectRole::None; NetworkObjectRole role = NetworkObjectRole::None;
if (obj) if (obj && NetworkManager::IsConnected())
{ {
ScopeLock lock(ObjectsLock); ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID()); const auto it = Objects.Find(obj->GetID());
@@ -903,9 +940,16 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj)
{ {
if (item.HasOwnership) if (item.HasOwnership)
role = item.Role; role = item.Role;
#if USE_NETWORK_REPLICATOR_LOG
return role;
#else
break; break;
#endif
} }
} }
#if USE_NETWORK_REPLICATOR_LOG
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to get ownership of unregistered network object {} ({})", obj->GetID(), obj->GetType().ToString());
#endif
} }
} }
return role; return role;
@@ -913,10 +957,11 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj)
void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole, bool hierarchical) void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole, bool hierarchical)
{ {
if (!obj) if (!obj || NetworkManager::IsOffline())
return; return;
const Guid objectId = obj->GetID();
ScopeLock lock(ObjectsLock); ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID()); const auto it = Objects.Find(objectId);
if (it == Objects.End()) if (it == Objects.End())
{ {
// Special case if we're just spawning this object // Special case if we're just spawning this object
@@ -944,8 +989,9 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
break; break;
} }
} }
return;
} }
else
{
auto& item = it->Item; auto& item = it->Item;
if (item.Object != obj) if (item.Object != obj)
return; return;
@@ -970,15 +1016,22 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
item.Role = localRole; item.Role = localRole;
} }
}
// Go down hierarchy // Go down hierarchy
if (hierarchical) if (hierarchical)
{ {
for (auto& e : Objects) for (auto& e : Objects)
{ {
if (e.Item.ParentId == item.ObjectId) if (e.Item.ParentId == objectId)
SetObjectOwnership(e.Item.Object.Get(), ownerClientId, localRole, hierarchical); SetObjectOwnership(e.Item.Object.Get(), ownerClientId, localRole, hierarchical);
} }
for (const SpawnItem& spawnItem : SpawnQueue)
{
if (IsParentOf(spawnItem.Object, obj))
SetObjectOwnership(spawnItem.Object, ownerClientId, localRole, hierarchical);
}
} }
} }
@@ -1182,9 +1235,11 @@ void NetworkInternal::NetworkReplicatorUpdate()
{ {
if (!q.HasOwnership && IsParentOf(q.Object, e.Object)) if (!q.HasOwnership && IsParentOf(q.Object, e.Object))
{ {
// Inherit ownership
q.HasOwnership = true; q.HasOwnership = true;
q.Role = e.Role; q.Role = e.Role;
q.OwnerClientId = e.OwnerClientId; q.OwnerClientId = e.OwnerClientId;
break;
} }
} }
} }
@@ -1623,7 +1678,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
} }
} }
// Setup all newly spawned objects // Add all newly spawned objects
for (int32 i = 0; i < msgData.ItemsCount; i++) for (int32 i = 0; i < msgData.ItemsCount; i++)
{ {
auto& msgDataItem = msgDataItems[i]; auto& msgDataItem = msgDataItems[i];
@@ -1652,6 +1707,16 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
// Boost future lookups by using indirection // Boost future lookups by using indirection
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", msgDataItem.ObjectId, item.ToString(), obj->GetType().ToString()); NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", msgDataItem.ObjectId, item.ToString(), obj->GetType().ToString());
IdsRemappingTable.Add(msgDataItem.ObjectId, item.ObjectId); IdsRemappingTable.Add(msgDataItem.ObjectId, item.ObjectId);
}
// Spawn all newly spawned objects (ensure to have valid ownership hierarchy set before spawning object)
for (int32 i = 0; i < msgData.ItemsCount; i++)
{
auto& msgDataItem = msgDataItems[i];
ScriptingObject* obj = objects[i];
auto it = Objects.Find(obj->GetID());
auto& item = it->Item;
const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId);
// Automatic parenting for scene objects // Automatic parenting for scene objects
auto sceneObject = ScriptingObject::Cast<SceneObject>(obj); auto sceneObject = ScriptingObject::Cast<SceneObject>(obj);

View File

@@ -68,7 +68,7 @@ public:
/// <remarks>Does nothing if network is offline.</remarks> /// <remarks>Does nothing if network is offline.</remarks>
/// <param name="obj">The object to replicate.</param> /// <param name="obj">The object to replicate.</param>
/// <param name="parent">The parent of the object (eg. player that spawned it).</param> /// <param name="parent">The parent of the object (eg. player that spawned it).</param>
API_FUNCTION() static void AddObject(ScriptingObject* obj, ScriptingObject* parent = nullptr); API_FUNCTION() static void AddObject(ScriptingObject* obj, const ScriptingObject* parent = nullptr);
/// <summary> /// <summary>
/// Removes the object from the network replication system. /// Removes the object from the network replication system.
@@ -80,14 +80,14 @@ public:
/// <summary> /// <summary>
/// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab). /// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab).
/// </summary> /// </summary>
/// <remarks>Does nothing if network is offline.</remarks> /// <remarks>Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system.</remarks>
/// <param name="obj">The object to spawn on other clients.</param> /// <param name="obj">The object to spawn on other clients.</param>
API_FUNCTION() static void SpawnObject(ScriptingObject* obj); API_FUNCTION() static void SpawnObject(ScriptingObject* obj);
/// <summary> /// <summary>
/// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab). /// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab).
/// </summary> /// </summary>
/// <remarks>Does nothing if network is offline.</remarks> /// <remarks>Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system.</remarks>
/// <param name="obj">The object to spawn on other clients.</param> /// <param name="obj">The object to spawn on other clients.</param>
/// <param name="clientIds">List with network client IDs that should receive network spawn event. Empty to spawn on all clients.</param> /// <param name="clientIds">List with network client IDs that should receive network spawn event. Empty to spawn on all clients.</param>
API_FUNCTION() static void SpawnObject(ScriptingObject* obj, const DataContainer<uint32>& clientIds); API_FUNCTION() static void SpawnObject(ScriptingObject* obj, const DataContainer<uint32>& clientIds);
@@ -99,6 +99,13 @@ public:
/// <param name="obj">The object to despawn on other clients.</param> /// <param name="obj">The object to despawn on other clients.</param>
API_FUNCTION() static void DespawnObject(ScriptingObject* obj); API_FUNCTION() static void DespawnObject(ScriptingObject* obj);
/// <summary>
/// Checks if the network object is spawned or added to the network replication system.
/// </summary>
/// <param name="obj">The network object.</param>
/// <returns>True if object exists in networking, otherwise false.</returns>
API_FUNCTION() static bool HasObject(const ScriptingObject* obj);
/// <summary> /// <summary>
/// Gets the Client Id of the network object owner. /// Gets the Client Id of the network object owner.
/// </summary> /// </summary>

View File

@@ -17,17 +17,17 @@ class FontAsset;
/// <summary> /// <summary>
/// The text range. /// The text range.
/// </summary> /// </summary>
API_STRUCT() struct TextRange API_STRUCT(NoDefault) struct TextRange
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
/// <summary> /// <summary>
/// The start index. /// The start index (inclusive).
/// </summary> /// </summary>
API_FIELD() int32 StartIndex; API_FIELD() int32 StartIndex;
/// <summary> /// <summary>
/// The end index. /// The end index (exclusive).
/// </summary> /// </summary>
API_FIELD() int32 EndIndex; API_FIELD() int32 EndIndex;
@@ -70,7 +70,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
/// <summary> /// <summary>
/// Gets the substring from the source text. /// Gets the substring from the source text.
/// </summary> /// </summary>
/// <param name="other">The text.</param> /// <param name="text">The text.</param>
/// <returns>The substring of the original text of the defined range.</returns> /// <returns>The substring of the original text of the defined range.</returns>
StringView Substring(const StringView& text) const StringView Substring(const StringView& text) const
{ {
@@ -87,7 +87,7 @@ struct TIsPODType<TextRange>
/// <summary> /// <summary>
/// The font line info generated during text processing. /// The font line info generated during text processing.
/// </summary> /// </summary>
API_STRUCT() struct FontLineCache API_STRUCT(NoDefault) struct FontLineCache
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
@@ -151,7 +151,7 @@ struct TIsPODType<FontLineCache>
/// <summary> /// <summary>
/// The cached font character entry (read for rendering and further processing). /// The cached font character entry (read for rendering and further processing).
/// </summary> /// </summary>
API_STRUCT() struct FontCharacterEntry API_STRUCT(NoDefault) struct FontCharacterEntry
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);

View File

@@ -10,7 +10,7 @@ namespace FlaxEngine.GUI
public class Button : ContainerControl public class Button : ContainerControl
{ {
/// <summary> /// <summary>
/// The default height fro the buttons. /// The default height for the buttons.
/// </summary> /// </summary>
public const float DefaultHeight = 24.0f; public const float DefaultHeight = 24.0f;