Files
EndlessHostRoles/Modules/CustomRpcSender.cs
T
2024-03-21 19:57:01 +01:00

296 lines
9.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using AmongUs.GameOptions;
using Hazel;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using InnerNet;
using System;
using System.Diagnostics.CodeAnalysis;
namespace EHR;
public class CustomRpcSender
{
public MessageWriter stream;
public readonly string name;
public readonly SendOption sendOption;
public bool isUnsafe;
public delegate void onSendDelegateType();
public onSendDelegateType onSendDelegate;
public State CurrentState
{
get { return currentState; }
set
{
if (isUnsafe) currentState = value;
else Logger.Warn("CurrentStateはisUnsafeがtrueの時のみ上書きできます", "CustomRpcSender");
}
}
private State currentState = State.BeforeInit;
//0~: targetClientId (GameDataTo)
//-1: 全プレイヤー (GameData)
//-2: 未設定
private int currentRpcTarget;
private CustomRpcSender()
{
}
public CustomRpcSender(string name, SendOption sendOption, bool isUnsafe)
{
stream = MessageWriter.Get(sendOption);
this.name = name;
this.sendOption = sendOption;
this.isUnsafe = isUnsafe;
currentRpcTarget = -2;
onSendDelegate = () => Logger.Info($"{this.name}'s onSendDelegate =>", "CustomRpcSender");
currentState = State.Ready;
Logger.Info($"\"{name}\" is ready", "CustomRpcSender");
}
public static CustomRpcSender Create(string name = "No Name Sender", SendOption sendOption = SendOption.None, bool isUnsafe = false)
{
return new(name, sendOption, isUnsafe);
}
#region Start/End Message
public CustomRpcSender StartMessage(int targetClientId = -1)
{
if (currentState != State.Ready)
{
string errorMsg = $"Messageを開始しようとしましたが、StateがReadyではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
if (targetClientId < 0)
{
// 全員に対するRPC
stream.StartMessage(5);
stream.Write(AmongUsClient.Instance.GameId);
}
else
{
// 特定のクライアントに対するRPC (Desync)
stream.StartMessage(6);
stream.Write(AmongUsClient.Instance.GameId);
stream.WritePacked(targetClientId);
}
currentRpcTarget = targetClientId;
currentState = State.InRootMessage;
return this;
}
[SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>")]
public CustomRpcSender EndMessage(int targetClientId = -1)
{
if (currentState != State.InRootMessage)
{
string errorMsg = $"Messageを終了しようとしましたが、StateがInRootMessageではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
stream.EndMessage();
currentRpcTarget = -2;
currentState = State.Ready;
return this;
}
#endregion
#region Start/End Rpc
public CustomRpcSender StartRpc(uint targetNetId, RpcCalls rpcCall)
=> StartRpc(targetNetId, (byte)rpcCall);
public CustomRpcSender StartRpc(
uint targetNetId,
byte callId)
{
if (currentState != State.InRootMessage)
{
string errorMsg = $"RPCを開始しようとしましたが、StateがInRootMessageではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
stream.StartMessage(2);
stream.WritePacked(targetNetId);
stream.Write(callId);
currentState = State.InRpc;
return this;
}
public CustomRpcSender EndRpc()
{
if (currentState != State.InRpc)
{
string errorMsg = $"RPCを終了しようとしましたが、StateがInRpcではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
stream.EndMessage();
currentState = State.InRootMessage;
return this;
}
#endregion
public CustomRpcSender AutoStartRpc(
uint targetNetId,
byte callId,
int targetClientId = -1)
{
if (targetClientId == -2) targetClientId = -1;
if (currentState is not State.Ready and not State.InRootMessage)
{
string errorMsg = $"RPCを自動で開始しようとしましたが、StateがReadyまたはInRootMessageではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
if (currentRpcTarget != targetClientId)
{
//StartMessage処理
if (currentState == State.InRootMessage) EndMessage();
StartMessage(targetClientId);
}
StartRpc(targetNetId, callId);
return this;
}
public void SendMessage()
{
if (currentState == State.InRootMessage) EndMessage();
if (currentState != State.Ready)
{
string errorMsg = $"RPCを送信しようとしましたが、StateがReadyではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
AmongUsClient.Instance.SendOrDisconnect(stream);
onSendDelegate();
currentState = State.Finished;
Logger.Info($"\"{name}\" is finished", "CustomRpcSender");
stream.Recycle();
}
// Write
#region PublicWriteMethods
public CustomRpcSender Write(float val) => Write(w => w.Write(val));
public CustomRpcSender Write(string val) => Write(w => w.Write(val));
public CustomRpcSender Write(ulong val) => Write(w => w.Write(val));
public CustomRpcSender Write(int val) => Write(w => w.Write(val));
public CustomRpcSender Write(uint val) => Write(w => w.Write(val));
public CustomRpcSender Write(ushort val) => Write(w => w.Write(val));
public CustomRpcSender Write(byte val) => Write(w => w.Write(val));
public CustomRpcSender Write(sbyte val) => Write(w => w.Write(val));
public CustomRpcSender Write(bool val) => Write(w => w.Write(val));
public CustomRpcSender Write(Il2CppStructArray<byte> bytes) => Write(w => w.Write(bytes));
public CustomRpcSender Write(Il2CppStructArray<byte> bytes, int offset, int length) => Write(w => w.Write(bytes, offset, length));
public CustomRpcSender WriteBytesAndSize(Il2CppStructArray<byte> bytes) => Write(w => w.WriteBytesAndSize(bytes));
public CustomRpcSender WritePacked(int val) => Write(w => w.WritePacked(val));
public CustomRpcSender WritePacked(uint val) => Write(w => w.WritePacked(val));
public CustomRpcSender WriteNetObject(InnerNetObject obj) => Write(w => w.WriteNetObject(obj));
#endregion
private CustomRpcSender Write(Action<MessageWriter> action)
{
if (currentState != State.InRpc)
{
string errorMsg = $"RPCを書き込もうとしましたが、StateがWrite(書き込み中)ではありません (in: \"{name}\")";
if (isUnsafe)
{
Logger.Warn(errorMsg, "CustomRpcSender.Warn");
}
else
{
throw new InvalidOperationException(errorMsg);
}
}
action(stream);
return this;
}
public enum State
{
BeforeInit = 0, //初期化前 何もできない
Ready, //送信準備完了 StartMessageとSendMessageを実行可能
InRootMessage, //StartMessage~EndMessageの間の状態 StartRpcとEndMessageを実行可能
InRpc, //StartRpc~EndRpcの間の状態 WriteとEndRpcを実行可能
Finished, //送信後 何もできない
}
}
public static class CustomRpcSenderExtensions
{
public static void RpcSetRole(this CustomRpcSender sender, PlayerControl player, RoleTypes role, int targetClientId = -1)
{
sender.AutoStartRpc(player.NetId, (byte)RpcCalls.SetRole, targetClientId)
.Write((ushort)role)
.EndRpc();
}
public static void RpcMurderPlayerV3(this CustomRpcSender sender, PlayerControl player, PlayerControl target, int targetClientId = -1)
{
sender.AutoStartRpc(player.NetId, (byte)RpcCalls.MurderPlayer, targetClientId)
.WriteNetObject(target)
.Write((byte)ExtendedPlayerControl.ResultFlags)
.EndRpc();
}
}