Skip to content

Commit

Permalink
Fix server authority RPC method bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeon8 committed Aug 27, 2024
1 parent dc171c1 commit 5d608cd
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 60 deletions.
4 changes: 2 additions & 2 deletions Sample/GameStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public override void Start()
private void NetworkManager_ServerStarted(object sender, System.EventArgs e)
{
if(NetworkManager.IsHost)
Spawner.SpawnEntities(PlayerPrefab, 0);
Spawner.SpawnEntity(PlayerPrefab, 0);
}

private void NetworkManager_ClientConnectedToServer(object sender, ServerConnectedEventArgs e)
{
Spawner.SpawnEntities(PlayerPrefab, e.Client.Id);
Spawner.SpawnEntity(PlayerPrefab, e.Client.Id);
}

public override void Update()
Expand Down
43 changes: 19 additions & 24 deletions StrideNet.SourceGenerator/CodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,47 +9,42 @@ internal class CodeBuilder
private StringBuilder _builder = new StringBuilder();

private const int TabLength = 4;
private int _spacesCount = 0;
private int _indentionLevel = 1;
private string CurrentIndention => new(' ', _indentionLevel * TabLength);

public CodeBuilder(){}
public CodeBuilder(int tabLevel)
{
_spacesCount = tabLevel * TabLength;
}

private string CurrentIndention => new(' ', _spacesCount);
public CodeBuilder(int indentionLevel) => _indentionLevel = indentionLevel;

public void Append(string value) => _builder.Append(PreAppendIndetion(value));

public void AppendLine() => _builder.AppendLine();

public void Append(string value) => _builder.Append(AddIndention(value));
public void AppendLine(string value) => _builder.AppendLine(PreAppendIndetion(value));

public void AppendLine(string value) => _builder.AppendLine(AddIndention(value));
public void AppendLineWidthTab(string value)
public void AppendBlock(string value)
{
AddTab();
_builder.AppendLine(AddIndention(value));
RemoveTab();
IncreaseIndention();
AppendLine(value);
DecreaseIndention();
}

private string AddIndention(string value)
private string PreAppendIndetion(string value)
{
return CurrentIndention + value.Replace("\n", "\n" + CurrentIndention);
}

public void AppendLine() => _builder.AppendLine();

public void AddTab()
public void IncreaseIndention()
{
_spacesCount += TabLength;
_indentionLevel++;
}

public void RemoveTab()
public void DecreaseIndention()
{
if(_spacesCount >= TabLength)
_spacesCount -= TabLength;
if(_indentionLevel > 1)
_indentionLevel--;
}

public override string ToString()
{
return _builder.ToString();
}
public override string ToString() => _builder.ToString();
}
}
66 changes: 43 additions & 23 deletions StrideNet.SourceGenerator/RpcGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ namespace {type.ContainingNamespace}
{{
public partial class {type.Name} : {type.BaseType}
{{
{wrappersBuilder}
protected override void RegisterRpcs()
{{
base.RegisterRpcs();
{registersBuilder}
base.RegisterRpcs();{registersBuilder}
}}
{wrappersBuilder}
}}
}}";
context.AddSource($"{type.Name}.g.cs", code);
Expand All @@ -70,7 +69,8 @@ private static void GenerateRpcRegistration(string methodName, AttributeData att
{
string rpcAuthority = attribute.GetNamedArgumentValue("Authority") ?? "NetworkAuthority.OwnerAuthority";
string sendMode = attribute.GetNamedArgumentValue("SendMode") ?? "MessageSendMode.Reliable";
builder.AppendLine(@$"RegisterRpc({methodName}, {rpcAuthority}, {sendMode});");
builder.AppendLine();
builder.Append(@$"RegisterRpc({methodName}, {rpcAuthority}, {sendMode});");
}

private static void GenerateCallWrapper(IMethodSymbol symbol, CodeBuilder builder)
Expand All @@ -79,13 +79,13 @@ private static void GenerateCallWrapper(IMethodSymbol symbol, CodeBuilder builde
string callMethodName = GetCallMethodName(methodName);
builder.AppendLine($"private static void {callMethodName}(Message message, NetworkScript script)");
builder.AppendLine("{");
builder.AddTab();
builder.IncreaseIndention();
foreach (var param in symbol.Parameters)
builder.AppendLine($"{param.Type} {param.Name} = message.Get<{param.Type}>();");

string argumentList = string.Join(",", symbol.Parameters.Select(p => p.Name));
builder.AppendLine($"(({symbol.ContainingType.Name})script).{methodName}({argumentList});");
builder.RemoveTab();
builder.DecreaseIndention();
builder.AppendLine("}");
builder.AppendLine();

Expand All @@ -94,33 +94,53 @@ private static void GenerateCallWrapper(IMethodSymbol symbol, CodeBuilder builde
private static void GenerateSendWrapper(MethodDeclarationSyntax syntax, IMethodSymbol symbol,
AttributeData attribute, CodeBuilder builder)
{
string paramsList = string.Join(", ", symbol.Parameters.Select(p => $"{p.Type} {p.Name}"));
string rpcMethodName = symbol.Name + "Rpc";
var callMethodName = GetCallMethodName(symbol.Name);
string methodParameters = string.Join(", ",
symbol.Parameters.Select(p => $"{p.Type} {p.Name}"));

builder.AppendLine($"{syntax.Modifiers} {symbol.ReturnType} {rpcMethodName}({paramsList})");
var rpcAuthority = attribute.GetNamedArgumentValue("Authority")
?? "StrideNet.NetworkAuthority.OwnerAuthority";

string arguments = string.Join(", ", symbol.Parameters.Select(p => p.Name));
string originalMethodCall = $"{symbol.Name}({arguments});";

var callMethodName = GetCallMethodName(symbol.Name);

builder.AppendLine($"{syntax.Modifiers} {symbol.ReturnType} {rpcMethodName}({methodParameters})");
builder.AppendLine("{");
builder.AddTab();
builder.IncreaseIndention();

var rpcAuthority = attribute.GetNamedArgumentValue("Authority") ?? "StrideNet.NetworkAuthority.OwnerAuthority";
if (rpcAuthority == "StrideNet.RpcMode.ServerAuthority")
{
builder.AppendLine("if(IsClient) return;");
if (rpcAuthority == "StrideNet.NetworkAuthority.ServerAuthority")
{
builder.AppendLine(originalMethodCall);
builder.AppendLine(@"if (IsClient)");
builder.AppendBlock(@"RpcExceptions.ThrowCalledServerAuthorative();");
}
else
{
builder.AppendLine(@$"if (IsServer)
{{
{originalMethodCall}
if(IsOwner)
return;
}}");

if (rpcAuthority == "StrideNet.NetworkAuthority.OwnerAuthority")
{
builder.AppendLine("else if (!IsOwner)");
builder.AppendBlock("RpcExceptions.ThrowCalledForeignEntity();");

}
builder.AppendLine();
}
builder.AppendLine(GenerateOriginalMethodCall());

builder.AppendLine($"Message message = RpcSender.CreateRpcMessage({callMethodName});");
builder.AppendLine($"Message message = RpcSender.CreateRpcMessage({callMethodName});");
foreach (var param in symbol.Parameters)
builder.AppendLine($"message.Add({param.Name});");
builder.AppendLine($"RpcSender.SendRpcMessage(message);");
builder.RemoveTab();
builder.DecreaseIndention();
builder.AppendLine("}");

string GenerateOriginalMethodCall()
{
string argumentList = string.Join(",", symbol.Parameters.Select(p => p.Name));
return $"{symbol.Name}({argumentList});";
}

}

Expand Down
8 changes: 5 additions & 3 deletions StrideNet.SourceGenerator/VariableGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ public partial class {type.Name} : {type.BaseType}
{builder}
protected override void RegisterVaribles()
{{
base.RegisterVaribles();
{registerBuilder}
base.RegisterVaribles();{registerBuilder}
}}
}}
}}";
Expand All @@ -71,7 +70,8 @@ private void GenerateProperty(IFieldSymbol field, CodeBuilder builder, CodeBuild
string sendMode = attribute.GetNamedArgumentValue("SendMode") ?? "MessageSendMode.Reliable";
string rpcName = $"__Set{propertyName}Rpc";

registerBuilder.AppendLine($"RegisterRpc({rpcName}, NetworkAuthority.ServerAuthority, {sendMode});");
registerBuilder.AppendLine();
registerBuilder.Append($"RegisterRpc({rpcName}, NetworkAuthority.ServerAuthority, {sendMode});");
GeneratePropertyAndMethods(field, builder, attribute, propertyName, rpcName);
}

Expand All @@ -83,6 +83,8 @@ private void GeneratePropertyAndMethods(IFieldSymbol field, CodeBuilder builder,
get => {field.Name};
set
{{
if(IsClient)
RpcExceptions.ThrowSettingVaribleFromClient();
{field.Name} = value;
Message message = RpcSender.CreateRpcMessage({rpcName});
message.Add(value);
Expand Down
18 changes: 17 additions & 1 deletion StrideNet/NetworkRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,26 @@ namespace StrideNet
/// <param name="script">Network script of RPC method.</param>
public delegate void RpcDelegate(Message message, NetworkScript script);

internal interface INetworkRpc
/// <summary>
/// Network RPC instance.
/// </summary>
public interface INetworkRpc
{
/// <summary>
/// RPC call authority.
/// </summary>
NetworkAuthority Mode { get; }

/// <summary>
/// RPC send mode.
/// </summary>
MessageSendMode SendMode { get; }

/// <summary>
/// Call received RPC with message.
/// </summary>
/// <param name="message">Received network message.</param>
/// <param name="script">Script instance for RPC method.</param>
void Call(Message message, NetworkScript script);
}

Expand Down
10 changes: 5 additions & 5 deletions StrideNet/NetworkSpawner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,20 @@ private static Message CreateSpawnMessage(ushort entityId, ushort ownerId)

#region Spawning
/// <summary>
/// Spawns entity on peers by prefab.
/// Spawns entity on all peers from prefab.
/// Prefab should be present in <see cref="SpawnablePrefabs"/> collection to be spawned.
/// </summary>
/// <param name="prefab">Prefab that contains network entities to spawn</param>
/// <param name="ownerId">Identificator of the peer that will have ownership over this entity.</param>
/// <exception cref="ArgumentException">Prefab is not present in <see cref="SpawnablePrefabs"/> collection</exception>
public List<Entity> SpawnEntities(Prefab prefab, ushort ownerId)
public List<Entity> SpawnEntity(Prefab prefab, ushort ownerId)
{
int prefabId = SpawnablePrefabs.IndexOf(prefab);
if (prefabId == -1)
throw new ArgumentException("Cannot spawn entity that is not present in SpawnablePrefabs collection.");

_networkManager.Send(CreateSpawnMessage((ushort)prefabId, ownerId));
return InstantiateEntities(prefab, (ushort)prefabId, ownerId);
return InstantiateEntity(prefab, (ushort)prefabId, ownerId);
}

private void InstantiatePrefab(ushort prefabId, ushort ownerId)
Expand All @@ -116,10 +116,10 @@ private void InstantiatePrefab(ushort prefabId, ushort ownerId)
}

Log.Info($"Spawning prefab {prefabId} with id: {ownerId}");
InstantiateEntities(prefab, prefabId, ownerId);
InstantiateEntity(prefab, prefabId, ownerId);
}

private List<Entity> InstantiateEntities(Prefab prefab, ushort prefabId, ushort ownerId)
private List<Entity> InstantiateEntity(Prefab prefab, ushort prefabId, ushort ownerId)
{
List<Entity> entities = prefab.Instantiate();
foreach (var entity in entities)
Expand Down
23 changes: 23 additions & 0 deletions StrideNet/RpcExceptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Linq;

namespace StrideNet
{
public static class RpcExceptions
{
public static void ThrowCalledServerAuthorative()
{
throw new InvalidOperationException("The RPC cannot be called from client, because it is sever authoritive.");
}

public static void ThrowCalledForeignEntity()
{
throw new InvalidOperationException("The RPC cannot be called from client that doesn't own entity.");
}

public static void ThrowSettingVaribleFromClient ()
{
throw new InvalidOperationException("Setting varible from client is not allowed.");
}
}
}
7 changes: 5 additions & 2 deletions StrideNet/RpcSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private Message CreateRpcMessage(int rpcId, MessageSendMode sendMode)
public Message CreateRpcMessage(RpcDelegate rpcDelegate)
{
if (!_registry.TryGetRpc(rpcDelegate, out INetworkRpc? rpc, out int rpcId))
throw new ArgumentException("Cannot create unregistered RPC.");
throw new ArgumentException("Cannot create message for unregistered RPC.");

return CreateRpcMessage(rpcId, rpc.SendMode);
}
Expand All @@ -50,6 +50,9 @@ public Message CreateRpcMessage(RpcDelegate rpcDelegate)
/// Sends RPC message over network.
/// </summary>
/// <param name="message">Message to send</param>
public void SendRpcMessage(Message message) => _networkManager.Send(message);
public void SendRpcMessage(Message message)
{
_networkManager.Send(message);
}
}
}

0 comments on commit 5d608cd

Please sign in to comment.