Skip to content

Commit

Permalink
Restructuring scene heirarchy to reduce dangerous object castings. (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidadas authored Nov 20, 2024
1 parent 2fd3385 commit b9bc666
Show file tree
Hide file tree
Showing 54 changed files with 1,749 additions and 1,039 deletions.
47 changes: 47 additions & 0 deletions Assets/Scripts/Core/BaseGameEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using ObjectExtensions;

[PersistableObject]
public class BaseGameEntity : IGameEntity
{
private string _instanceId;

[CloneIgnore]
public string InstanceID
{
get => _instanceId ??= Guid.NewGuid().ToString().Replace("-", "");
set => _instanceId ??= value;
}

public string TypeID { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }

/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetInstanceID()
{
return InstanceID;
}

/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetTypeID()
{
return TypeID;
}

/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetDisplayName()
{
return DisplayName;
}
}
172 changes: 172 additions & 0 deletions Assets/Scripts/Core/BaseSceneNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;

/// <summary>
/// Represents an abstract scene node in the game.
/// </summary>
public abstract class BaseSceneNode : BaseGameEntity, ISceneNode
{
// Parent Info
[CloneIgnore]
public string ParentInstanceID { get; set; }

[CloneIgnore]
public string LastParentInstanceID { get; set; }

[CloneIgnore]
[PersistableIgnore]
public ISceneNode ParentNode { get; set; }

[CloneIgnore]
[PersistableIgnore]
public ISceneNode LastParentNode { get; set; }

// Owner Info
private string _ownerInstanceId;

[CloneIgnore]
public string OwnerInstanceID
{
get => _ownerInstanceId;
set => SetOwnerInstanceID(value);
}
public List<string> AllowedOwnerInstanceIDs { get; set; }

/// <summary>
/// Default constructor.
/// </summary>
public BaseSceneNode() { }

/// <summary>
/// Sets the parent scene node of the current scene node.
/// </summary>
/// <param name="newParent">The parent scene node.</param>
public void SetParent(ISceneNode newParent)
{
if (ParentNode == newParent)
return;

ISceneNode oldParent = ParentNode;

// Remove from old parent.
if (oldParent != null)
{
oldParent.RemoveChild(this);
}

// Update parent references.
LastParentNode = oldParent;
ParentNode = newParent;
LastParentInstanceID = ParentInstanceID;
ParentInstanceID = newParent?.InstanceID;
}

/// <summary>
/// Gets the parent scene node of the current scene node.
/// </summary>
/// <returns>The parent scene node.</returns>
public ISceneNode GetParent()
{
return ParentNode;
}

/// <summary>
/// Returns the last parent scene node of the current scene node.
/// </summary>
/// <returns>The last parent scene node.</returns>
public ISceneNode GetLastParent()
{
return LastParentNode;
}

/// <summary>
/// Returns the closest parent scene node of the specified type.
/// </summary>
/// <typeparam name="T">The type of the parent scene node.</typeparam>
/// <returns>The closest parent scene node of the specified type.</returns>
public T GetParentOfType<T>()
where T : class, ISceneNode
{
// Check if the current scene node is the specified type.
if (this is T matchingSelf)
{
return matchingSelf;
}

// Check the parent scene nodes.
ISceneNode parent = ParentNode;
HashSet<ISceneNode> visitedNodes = new HashSet<ISceneNode>();

while (parent != null)
{
if (!visitedNodes.Add(parent))
{
// We've encountered this node before, which indicates a cycle.
throw new InvalidOperationException("Cycle detected in scene graph.");
}

if (parent is T matchingParent)
{
return matchingParent;
}

parent = parent.GetParent();
}

// No parent of the specified type was found.
return null;
}

/// <summary>
/// Sets the owner type id. If the ID is not in the allowed list, throws an exception.
/// </summary>
/// <param name="value">The owner type id to set.</param>
/// <exception cref="GameStateException">Thrown when the owner type id is invalid.</exception>
private void SetOwnerInstanceID(string value)
{
if (
AllowedOwnerInstanceIDs == null
|| AllowedOwnerInstanceIDs.Count == 0
|| AllowedOwnerInstanceIDs.Contains(value)
)
{
_ownerInstanceId = value;
}
else
{
throw new GameStateException(
$"Invalid owner type id \"{value}\" for object \"{DisplayName}\"."
);
}
}

/// <summary>
/// Returns all children of the current scene node that match the specified game owner id and type.
/// </summary>
/// <typeparam name="T">The type of the children to retrieve.</typeparam>
/// <param name="ownerInstanceId">The game owner id to match.</param>
/// <returns>An enumerable collection of children that match the specified game owner id and type.</returns>
public IEnumerable<T> GetChildrenByOwnerInstanceID<T>(string ownerInstanceId)
where T : class, ISceneNode
{
List<T> matchingChildren = new List<T>();

Traverse(node =>
{
if (node is T && node.OwnerInstanceID == ownerInstanceId)
{
matchingChildren.Add((T)node);
}
});

return matchingChildren;
}

public abstract void AddChild(ISceneNode child);

public abstract void RemoveChild(ISceneNode child);

public abstract IEnumerable<ISceneNode> GetChildren();

public abstract void Traverse(Action<ISceneNode> action);
}
15 changes: 15 additions & 0 deletions Assets/Scripts/Core/ContainerNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;

public abstract class ContainerNode : BaseSceneNode
{
public override void Traverse(Action<ISceneNode> action)
{
action(this);

foreach (var child in GetChildren())
{
child.Traverse(action);
}
}
}
54 changes: 0 additions & 54 deletions Assets/Scripts/Core/GameEntity.cs

This file was deleted.

28 changes: 28 additions & 0 deletions Assets/Scripts/Core/IGameEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/// <summary>
///
/// </summary>
public interface IGameEntity
{
public string InstanceID { get; set; }
public string TypeID { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }

/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetInstanceID();

/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetTypeID();

/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetDisplayName();
}
Loading

0 comments on commit b9bc666

Please sign in to comment.