Merge SyncObject and SyncProperty attributes

- New attribute name: Sync
- No longer provide Scene or Container
  information to Sync attribute
- Scene name comes from Type name
- Container node is simply /Game/World
- Remove unnecessary container
  properties from Game class
main
copygirl 5 years ago
parent e08ba2ca37
commit e62d7f5ee6
  1. 6
      scene/GameScene.tscn
  2. 4
      src/Network/NetworkRPC.cs
  3. 4
      src/Network/Sync.cs
  4. 4
      src/Network/SyncClient.cs
  5. 30
      src/Network/SyncRegistry.cs
  6. 6
      src/Network/SyncServer.cs
  7. 6
      src/Objects/Block.cs
  8. 8
      src/Objects/Player.cs
  9. 12
      src/Scenes/Game.cs

@ -5,11 +5,5 @@
[node name="Game" type="Node2D"] [node name="Game" type="Node2D"]
pause_mode = 2 pause_mode = 2
script = ExtResource( 3 ) script = ExtResource( 3 )
PlayerContainerPath = NodePath("World/Players")
BlockContainerPath = NodePath("World/Blocks")
[node name="World" type="Node" parent="."] [node name="World" type="Node" parent="."]
[node name="Players" type="Node" parent="World"]
[node name="Blocks" type="Node" parent="World"]

@ -89,8 +89,8 @@ public static class NetworkRPC
var rpc = method.GetCustomAttribute<RPCAttribute>(); var rpc = method.GetCustomAttribute<RPCAttribute>();
if (rpc == null) continue; if (rpc == null) continue;
if (!method.IsStatic && (type.GetCustomAttribute<SyncObjectAttribute>() == null)) throw new Exception( if (!method.IsStatic && (type.GetCustomAttribute<SyncAttribute>() == null)) throw new Exception(
$"Type of non-static RPC method '{method.DeclaringType}.{method.Name}' must have {nameof(SyncObjectAttribute)}"); $"Type of non-static RPC method '{method.DeclaringType}.{method.Name}' must have {nameof(SyncAttribute)}");
var deSerializers = new List<IDeSerializer>(); var deSerializers = new List<IDeSerializer>();
var paramEnumerable = ((IEnumerable<ParameterInfo>)method.GetParameters()).GetEnumerator(); var paramEnumerable = ((IEnumerable<ParameterInfo>)method.GetParameters()).GetEnumerator();

@ -23,8 +23,8 @@ public class Sync
public SyncStatus GetStatusOrNull(Node obj) public SyncStatus GetStatusOrNull(Node obj)
{ {
if (obj.GetType().GetCustomAttribute<SyncObjectAttribute>() == null) if (obj.GetType().GetCustomAttribute<SyncAttribute>() == null)
throw new ArgumentException($"Type {obj.GetType()} is missing {nameof(SyncObjectAttribute)}"); throw new ArgumentException($"Type {obj.GetType()} is missing {nameof(SyncAttribute)}");
return StatusByObject.TryGetValue(obj, out var value) ? value : null; return StatusByObject.TryGetValue(obj, out var value) ? value : null;
} }
public SyncStatus GetStatusOrThrow(Node obj) public SyncStatus GetStatusOrThrow(Node obj)

@ -20,11 +20,11 @@ public class SyncClient : Sync
if (packetObj.Mode != SyncMode.Spawn) throw new Exception( if (packetObj.Mode != SyncMode.Spawn) throw new Exception(
$"Unknown synced object {info.Name} (ID {packetObj.SyncID})"); $"Unknown synced object {info.Name} (ID {packetObj.SyncID})");
var obj = info.InstanceScene.Init<Node>(); var obj = info.Scene.Init<Node>();
status = new SyncStatus(packetObj.SyncID, obj, info); status = new SyncStatus(packetObj.SyncID, obj, info);
StatusBySyncID.Add(status.SyncID, status); StatusBySyncID.Add(status.SyncID, status);
StatusByObject.Add(status.Object, status); StatusByObject.Add(status.Object, status);
Client.GetNode(info.ContainerNodePath).AddChild(obj); Client.GetNode("World").AddChild(obj);
} else { } else {
if (packetObj.Mode == SyncMode.Spawn) throw new Exception( if (packetObj.Mode == SyncMode.Spawn) throw new Exception(
$"Spawning object {info.Name} with ID {packetObj.SyncID}, but it already exists"); $"Spawning object {info.Name} with ID {packetObj.SyncID}, but it already exists");

@ -11,15 +11,15 @@ public static class SyncRegistry
static SyncRegistry() static SyncRegistry()
{ {
foreach (var type in typeof(SyncRegistry).Assembly.GetTypes()) { foreach (var type in typeof(SyncRegistry).Assembly.GetTypes()) {
var objAttr = type.GetCustomAttribute<SyncObjectAttribute>(); var objAttr = type.GetCustomAttribute<SyncAttribute>();
if (objAttr == null) continue; if (objAttr == null) continue;
if (!typeof(Node).IsAssignableFrom(type)) throw new Exception( if (!typeof(Node).IsAssignableFrom(type)) throw new Exception(
$"Type {type} with {nameof(SyncObjectAttribute)} must be a subclass of {nameof(Node)}"); $"Type {type} with {nameof(SyncAttribute)} must be a subclass of {nameof(Node)}");
var objInfo = new SyncObjectInfo((ushort)_byID.Count, type); var objInfo = new SyncObjectInfo((ushort)_byID.Count, type);
foreach (var property in type.GetProperties()) { foreach (var property in type.GetProperties()) {
if (property.GetCustomAttribute<SyncPropertyAttribute>() == null) continue; if (property.GetCustomAttribute<SyncAttribute>() == null) continue;
var propType = typeof(SyncPropertyInfo<,>).MakeGenericType(type, property.PropertyType); var propType = typeof(SyncPropertyInfo<,>).MakeGenericType(type, property.PropertyType);
var propInfo = (SyncPropertyInfo)Activator.CreateInstance(propType, (byte)objInfo.PropertiesByID.Count, property); var propInfo = (SyncPropertyInfo)Activator.CreateInstance(propType, (byte)objInfo.PropertiesByID.Count, property);
objInfo.PropertiesByID.Add(propInfo); objInfo.PropertiesByID.Add(propInfo);
@ -41,7 +41,7 @@ public static class SyncRegistry
=> Get(typeof(T)); => Get(typeof(T));
public static SyncObjectInfo Get(Type type) public static SyncObjectInfo Get(Type type)
=> _byType.TryGetValue(type, out var value) ? value : throw new Exception( => _byType.TryGetValue(type, out var value) ? value : throw new Exception(
$"No {nameof(SyncObjectInfo)} found for type {type} (missing {nameof(SyncObjectAttribute)}?)"); $"No {nameof(SyncObjectInfo)} found for type {type} (missing {nameof(SyncAttribute)}?)");
} }
@ -49,11 +49,9 @@ public class SyncObjectInfo
{ {
public ushort ID { get; } public ushort ID { get; }
public Type Type { get; } public Type Type { get; }
public PackedScene Scene { get; }
public string Name => Type.Name; public string Name => Type.Name;
public PackedScene InstanceScene { get; }
public string ContainerNodePath { get; }
public List<SyncPropertyInfo> PropertiesByID { get; } = new List<SyncPropertyInfo>(); public List<SyncPropertyInfo> PropertiesByID { get; } = new List<SyncPropertyInfo>();
public Dictionary<string, SyncPropertyInfo> PropertiesByName { get; } = new Dictionary<string, SyncPropertyInfo>(); public Dictionary<string, SyncPropertyInfo> PropertiesByName { get; } = new Dictionary<string, SyncPropertyInfo>();
@ -61,10 +59,7 @@ public class SyncObjectInfo
{ {
ID = id; ID = id;
Type = type; Type = type;
Scene = GD.Load<PackedScene>($"res://scene/{type.Name}.tscn");
var attr = type.GetCustomAttribute<SyncObjectAttribute>();
InstanceScene = GD.Load<PackedScene>($"res://scene/{attr.Scene}.tscn");
ContainerNodePath = attr.Container;
} }
} }
@ -95,16 +90,7 @@ public class SyncPropertyInfo<TObject, TValue> : SyncPropertyInfo
} }
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class SyncObjectAttribute : Attribute public class SyncAttribute : Attribute
{
public string Scene { get; }
public string Container { get; }
public SyncObjectAttribute(string scene, string container)
{ Scene = scene; Container = container; }
}
[AttributeUsage(AttributeTargets.Property)]
public class SyncPropertyAttribute : Attribute
{ {
} }

@ -17,12 +17,12 @@ public class SyncServer : Sync
where T : Node where T : Node
{ {
var info = SyncRegistry.Get<T>(); var info = SyncRegistry.Get<T>();
var obj = info.InstanceScene.Init<T>(); var obj = info.Scene.Init<T>();
var status = new SyncStatus(_syncIDCounter++, obj, info){ Mode = SyncMode.Spawn }; var status = new SyncStatus(_syncIDCounter++, obj, info){ Mode = SyncMode.Spawn };
StatusBySyncID.Add(status.SyncID, status); StatusBySyncID.Add(status.SyncID, status);
StatusByObject.Add(status.Object, status); StatusByObject.Add(status.Object, status);
_dirtyObjects.Add(status); _dirtyObjects.Add(status);
Server.GetNode(info.ContainerNodePath).AddChild(obj); Server.GetNode("World").AddChild(obj);
return obj; return obj;
} }
@ -45,7 +45,7 @@ public class SyncServer : Sync
{ {
var status = GetStatusOrThrow(obj); var status = GetStatusOrThrow(obj);
if (!status.Info.PropertiesByName.TryGetValue(property, out var propInfo)) throw new ArgumentException( if (!status.Info.PropertiesByName.TryGetValue(property, out var propInfo)) throw new ArgumentException(
$"No {nameof(SyncPropertyInfo)} found for {obj.GetType()}.{property} (missing {nameof(SyncPropertyAttribute)}?)", nameof(property)); $"No {nameof(SyncPropertyInfo)} found for {obj.GetType()}.{property} (missing {nameof(SyncAttribute)}?)", nameof(property));
if (!(obj.GetGame() is Server)) return; if (!(obj.GetGame() is Server)) return;
status.DirtyProperties |= 1 << propInfo.ID; status.DirtyProperties |= 1 << propInfo.ID;

@ -1,15 +1,15 @@
using Godot; using Godot;
[SyncObject("Block", "World/Blocks")] [Sync]
public class Block : StaticBody2D public class Block : StaticBody2D
{ {
[SyncProperty] [Sync]
public new BlockPos Position { public new BlockPos Position {
get => BlockPos.FromVector(base.Position); get => BlockPos.FromVector(base.Position);
set => base.Position = this.SetSync(value).ToVector(); set => base.Position = this.SetSync(value).ToVector();
} }
[SyncProperty] [Sync]
public Color Color { public Color Color {
get => Modulate; get => Modulate;
set => Modulate = this.SetSync(value); set => Modulate = this.SetSync(value);

@ -2,7 +2,7 @@ using System;
using Godot; using Godot;
// TODO: Maybe figure out how we can make different classes (LocalPlayer, NPCPlayer) synchronizable. // TODO: Maybe figure out how we can make different classes (LocalPlayer, NPCPlayer) synchronizable.
[SyncObject("Player", "World/Players")] [Sync]
public class Player : KinematicBody2D, IInitializer public class Player : KinematicBody2D, IInitializer
{ {
[Export] public NodePath DisplayNamePath { get; set; } [Export] public NodePath DisplayNamePath { get; set; }
@ -14,19 +14,19 @@ public class Player : KinematicBody2D, IInitializer
public bool IsLocal { get; private set; } = false; public bool IsLocal { get; private set; } = false;
[SyncProperty] [Sync]
public new Vector2 Position { public new Vector2 Position {
get => base.Position; get => base.Position;
set { if (!IsLocal) base.Position = this.SetSync(value); } set { if (!IsLocal) base.Position = this.SetSync(value); }
} }
[SyncProperty] [Sync]
public Color Color { public Color Color {
get => Sprite.Modulate; get => Sprite.Modulate;
set => Sprite.Modulate = this.SetSync(value); set => Sprite.Modulate = this.SetSync(value);
} }
[SyncProperty] [Sync]
public string DisplayName { public string DisplayName {
get => DisplayNameLabel.Text; get => DisplayNameLabel.Text;
set => DisplayNameLabel.Text = this.SetSync(value); set => DisplayNameLabel.Text = this.SetSync(value);

@ -6,22 +6,10 @@ public abstract class Game : Node2D
{ {
public Sync Sync { get; protected set; } public Sync Sync { get; protected set; }
[Export] public NodePath PlayerContainerPath { get; set; }
[Export] public NodePath BlockContainerPath { get; set; }
public Node PlayerContainer { get; private set; }
public Node BlockContainer { get; private set; }
// Using _EnterTree to make sure this code runs before any other. // Using _EnterTree to make sure this code runs before any other.
public override void _EnterTree() public override void _EnterTree()
=> GD.Randomize(); => GD.Randomize();
public override void _Ready()
{
PlayerContainer = GetNode(PlayerContainerPath);
BlockContainer = GetNode(BlockContainerPath);
}
// NOTE: When multithreaded physics are enabled, DirectSpaceState can only be used in _PhysicsProcess. // NOTE: When multithreaded physics are enabled, DirectSpaceState can only be used in _PhysicsProcess.
public Block GetBlockAt(BlockPos position) public Block GetBlockAt(BlockPos position)
=> GetWorld2d().DirectSpaceState.IntersectPoint(position.ToVector()).Cast<Dictionary>() => GetWorld2d().DirectSpaceState.IntersectPoint(position.ToVector()).Cast<Dictionary>()

Loading…
Cancel
Save