Răsfoiți Sursa

Merge pull request #696 from BennAbbot/main-phase-action-rpc-rework

Main Phase Action RPC Rework
x89rt2 4 luni în urmă
părinte
comite
2483ae1490

+ 40 - 49
Assets/Scripts/Script/GManager.cs

@@ -492,9 +492,29 @@ public class GManager : MonoBehaviourPun
         AllowAlphaInputs();
     }
 
+    public Player GetPlayerFromID(int playerID)
+    {
+        if (You.PlayerID == playerID)
+        {
+            return You;
+        }
+        else if (Opponent.PlayerID == playerID)
+        {
+            return Opponent;
+        }
+
+        return null;
+    }
+
+    #region Cheats
+    public bool AllowCheats()
+    {
+        return !ContinuousController.instance.isRandomMatch || ContinuousController.instance.isAI;
+    }
+
     void AllowAlphaInputs()
     {
-        if (ContinuousController.instance.isRandomMatch && !ContinuousController.instance.isAI)
+        if (!AllowCheats())
             return;
 
         if (turnStateMachine == null)
@@ -506,79 +526,49 @@ public class GManager : MonoBehaviourPun
         //Draw a card
         if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.D))
         {
-            photonView.RPC("DrawCardRPC", RpcTarget.Others);
-            StartCoroutine(DrawCard(You));
+            turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.Draw));
         }
 
         //Trash a card
         if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.T))
         {
-            photonView.RPC("TrashCardRPC", RpcTarget.Others);
-            StartCoroutine(TrashCard(You));
+            turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.TrashCard));
         }
 
         //Top deck a card
         if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.L))
         {
-            photonView.RPC("TopDeckCardRPC", RpcTarget.Others);
-            StartCoroutine(TopDeckCard(You));
+            turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.PlaceCardOnDeck));
         }
 
         //Place Top Security
         if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.R))
         {
             bool keyInput = Input.GetKey(KeyCode.LeftShift);
-            photonView.RPC("PlaceInSecurityRPC", RpcTarget.Others, keyInput);
-            StartCoroutine(PlaceInSecurity(You, keyInput));
+            if (keyInput)
+            {
+                turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.PlaceCardInSecurity));
+            }
+            else
+            {
+                turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.PlaceCardInSecurityFaceup));
+            }
         }
 
         //Gain Memory
         if(Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Equals))
         {
-            photonView.RPC("AlterMemoryRPC", RpcTarget.Others, -1);
-            StartCoroutine(AlterMemory(You, 1));
+            turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.GainMemory));
         }
 
         //Lose Memory
         if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Minus))
         {
-            photonView.RPC("AlterMemoryRPC", RpcTarget.Others, 1);
-            StartCoroutine(AlterMemory(You, -1));
+            turnStateMachine.QueueMainPhaseAction(You, new CheatAction(You.PlayerID, CheatAction.Type.LoseMemory));
         }
             
     }
 
-    [PunRPC]
-    public void DrawCardRPC()
-    {
-        StartCoroutine(DrawCard(Opponent));
-    }
-
-    [PunRPC]
-    public void TrashCardRPC()
-    {
-        StartCoroutine(TrashCard(Opponent));
-    }
-
-    [PunRPC]
-    public void TopDeckCardRPC()
-    {
-        StartCoroutine(TopDeckCard(Opponent));
-    }
-
-    [PunRPC]
-    public void PlaceInSecurityRPC(bool keyInput)
-    {
-        StartCoroutine(PlaceInSecurity(Opponent, keyInput));
-    }
-
-    [PunRPC]
-    public void AlterMemoryRPC(int value)
-    {
-        StartCoroutine(AlterMemory(Opponent,value));
-    }
-
-
     public IEnumerator DrawCard(Player _player)
     {
         yield return StartCoroutine(new DrawClass(_player, 1, null).Draw());
@@ -586,16 +576,16 @@ public class GManager : MonoBehaviourPun
         yield return StartCoroutine(turnStateMachine.SetMainPhase());
     }
 
-    IEnumerator AlterMemory(Player _player, int value)
+    public IEnumerator AlterMemory(Player _player, int value)
     {
-        yield return StartCoroutine(You.AddMemory(value, null));
+        yield return StartCoroutine(_player.AddMemory(value, null));
 
         yield return StartCoroutine(turnStateMachine.SetMainPhase());
 
         yield return StartCoroutine(autoProcessing.EndTurnCheck());
     }
 
-    IEnumerator TrashCard(Player _player)
+    public IEnumerator TrashCard(Player _player)
     {
         SelectHandEffect selectHandEffect = GManager.instance.GetComponent<SelectHandEffect>();
 
@@ -618,7 +608,7 @@ public class GManager : MonoBehaviourPun
         yield return StartCoroutine(turnStateMachine.SetMainPhase());
     }
 
-    IEnumerator TopDeckCard(Player _player)
+    public IEnumerator TopDeckCard(Player _player)
     {
         SelectHandEffect selectHandEffect = GManager.instance.GetComponent<SelectHandEffect>();
 
@@ -641,7 +631,7 @@ public class GManager : MonoBehaviourPun
         yield return StartCoroutine(turnStateMachine.SetMainPhase());
     }
 
-    IEnumerator PlaceInSecurity(Player _player, bool placeFaceup)
+    public IEnumerator PlaceInSecurity(Player _player, bool placeFaceup)
     {
         CardSource selectedSource = null;
 
@@ -679,4 +669,5 @@ public class GManager : MonoBehaviourPun
 
         yield return StartCoroutine(turnStateMachine.SetMainPhase());
     }
+    #endregion
 }

+ 45 - 0
Assets/Scripts/Script/MainPhaseAction/ActivateCardAction.cs

@@ -0,0 +1,45 @@
+using ExitGames.Client.Photon;
+using UnityEngine;
+
+public class ActivateCardAction : MainPhaseAction
+{
+    int CardIndex;
+    int SkillIndex;
+
+    public ActivateCardAction(int cardIndex, int skillIndex)
+    {
+        CardIndex = cardIndex;
+        SkillIndex = skillIndex;
+    }
+
+    public ActivateCardAction(byte[] bytes)
+    {
+        CardIndex = -1;
+        SkillIndex = -1;
+        Deserialize(bytes);
+    }
+
+    public override void Execute(TurnStateMachine stateMachine)
+    {
+        stateMachine.SetActCardSkill(CardIndex, SkillIndex);
+    }
+
+    public override void Deserialize(byte[] bytes)
+    {
+        int index = 0;
+
+        Protocol.Deserialize(out CardIndex, bytes, ref index);
+        Protocol.Deserialize(out SkillIndex, bytes, ref index);
+    }
+
+    public override byte[] Serialize()
+    {
+        byte[] bytes = new byte[sizeof(int) * 2];
+        int index = 0;
+
+        Protocol.Serialize(CardIndex, bytes, ref index);
+        Protocol.Serialize(SkillIndex, bytes, ref index);
+
+        return bytes;
+    }
+}

+ 44 - 0
Assets/Scripts/Script/MainPhaseAction/ActivatePermanentAction.cs

@@ -0,0 +1,44 @@
+using ExitGames.Client.Photon;
+
+public class ActivatePermanentAction : MainPhaseAction
+{
+    int PermanentIndex;
+    int SkillIndex;
+
+    public ActivatePermanentAction(int permanentIndex, int skillIndex)
+    {
+        PermanentIndex = permanentIndex;
+        SkillIndex = skillIndex;
+    }
+
+    public ActivatePermanentAction(byte[] bytes)
+    {
+        PermanentIndex = -1;
+        SkillIndex = -1;
+        Deserialize(bytes);
+    }
+
+    public override void Execute(TurnStateMachine stateMachine)
+    {
+        stateMachine.SetActSkill(PermanentIndex, SkillIndex);
+    }
+
+    public override void Deserialize(byte[] bytes)
+    {
+        int index = 0;
+
+        Protocol.Deserialize(out PermanentIndex, bytes, ref index);
+        Protocol.Deserialize(out SkillIndex, bytes, ref index);
+    }
+
+    public override byte[] Serialize()
+    {
+        byte[] bytes = new byte[sizeof(int) * 2];
+        int index = 0;
+
+        Protocol.Serialize(PermanentIndex, bytes, ref index);
+        Protocol.Serialize(SkillIndex, bytes, ref index);
+
+        return bytes;
+    }
+}

+ 45 - 0
Assets/Scripts/Script/MainPhaseAction/AttackPermanentAction.cs

@@ -0,0 +1,45 @@
+using ExitGames.Client.Photon;
+
+public class AttackPermanentAction : MainPhaseAction
+{
+    int PermanentIndex;
+    int AttackTargetPermanentIndex;
+
+    public AttackPermanentAction(byte[] bytes)
+    {
+        PermanentIndex = -1;
+        AttackTargetPermanentIndex = -1;
+
+        Deserialize(bytes);
+    }
+
+    public AttackPermanentAction(int permanentIndex, int attackTargetPermanentIndex)
+    {
+        PermanentIndex = permanentIndex;
+        AttackTargetPermanentIndex = attackTargetPermanentIndex;
+    }
+
+    public override void Execute(TurnStateMachine stateMachine)
+    {
+        stateMachine.SetAttackingPermaent(PermanentIndex, AttackTargetPermanentIndex);
+    }
+
+    public override void Deserialize(byte[] bytes)
+    {
+        int index = 0;
+
+        Protocol.Deserialize(out PermanentIndex, bytes, ref index);
+        Protocol.Deserialize(out AttackTargetPermanentIndex, bytes, ref index);
+    }
+
+    public override byte[] Serialize()
+    {
+        byte[] bytes = new byte[sizeof(int) * 2];
+        int index = 0;
+
+        Protocol.Serialize(PermanentIndex, bytes, ref index);
+        Protocol.Serialize(AttackTargetPermanentIndex, bytes, ref index);
+
+        return bytes;
+    }
+}

+ 96 - 0
Assets/Scripts/Script/MainPhaseAction/CheatAction.cs

@@ -0,0 +1,96 @@
+using ExitGames.Client.Photon;
+
+public class CheatAction : MainPhaseAction
+{
+    public enum Type
+    {
+        None,
+        Draw,
+        TrashCard,
+        PlaceCardOnDeck,
+        PlaceCardInSecurity,
+        PlaceCardInSecurityFaceup,
+        GainMemory,
+        LoseMemory,
+    }
+
+    Type CheatType;
+    int PlayerID;
+
+    public CheatAction(int playerID, Type cheatType)
+    {
+        PlayerID = playerID;
+        CheatType = cheatType;
+    }
+
+    public CheatAction(byte[] bytes)
+    {
+        CheatType = Type.None;
+        PlayerID = -1;
+        Deserialize(bytes);
+    }
+
+    public override void Execute(TurnStateMachine stateMachine)
+    {
+        GManager gameManager = GManager.instance;
+
+        Player player = gameManager.GetPlayerFromID(PlayerID);
+
+        if (player == null)
+        {
+            return;
+        }
+
+        if (gameManager.AllowCheats())
+        {
+            switch (CheatType)
+            {
+                case Type.Draw:
+                    gameManager.StartCoroutine(gameManager.DrawCard(player));
+                    break;
+                case Type.TrashCard:
+                    gameManager.StartCoroutine(gameManager.TrashCard(player));
+                    break;
+                case Type.PlaceCardOnDeck:
+                    gameManager.StartCoroutine(gameManager.TopDeckCard(player));
+                    break;
+                case Type.PlaceCardInSecurity:
+                    gameManager.StartCoroutine(gameManager.PlaceInSecurity(player, false));
+                    break;
+                case Type.PlaceCardInSecurityFaceup:
+                    gameManager.StartCoroutine(gameManager.PlaceInSecurity(player, true));
+                    break;
+                case Type.GainMemory:
+                    gameManager.StartCoroutine(gameManager.AlterMemory(player, 1));
+                    break;
+                case Type.LoseMemory:
+                    gameManager.StartCoroutine(gameManager.AlterMemory(player, -1));
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    public override void Deserialize(byte[] bytes)
+    {
+        int index = 0;
+
+        Protocol.Deserialize(out int cheatTypeInt, bytes, ref index);
+        CheatType = (Type)cheatTypeInt;
+
+        Protocol.Deserialize(out PlayerID, bytes, ref index);
+    }
+
+
+    public override byte[] Serialize()
+    {
+        byte[] bytes = new byte[sizeof(int) * 2];
+        int index = 0;
+
+        Protocol.Serialize((int)CheatType, bytes, ref index);
+        Protocol.Serialize(PlayerID, bytes, ref index);
+
+        return bytes;
+    }
+}

+ 6 - 0
Assets/Scripts/Script/MainPhaseAction/MainPhaseAction.cs

@@ -0,0 +1,6 @@
+public abstract class MainPhaseAction : IGamePacket
+{
+    public abstract void Execute(TurnStateMachine stateMachine);
+    public abstract void Deserialize(byte[] bytes);
+    public abstract byte[] Serialize();
+}

+ 101 - 0
Assets/Scripts/Script/MainPhaseAction/PlayCardAction.cs

@@ -0,0 +1,101 @@
+using ExitGames.Client.Photon;
+
+public class PlayCardAction : MainPhaseAction
+{
+    int CardIndex;
+    int TargetFrameID;
+    int[] JogressEvoRootsFrameIDs;
+    int BurstTamerFrameID;
+    int[] AppFusionFrameIDs;
+
+    public PlayCardAction(int cardIndex, int targetFrameID, int[] jogressEvoRootsFrameIDs, int burstTamerFrameID, int[] appFusionFrameIDs)
+    {
+        CardIndex = cardIndex;
+        TargetFrameID = targetFrameID;
+        JogressEvoRootsFrameIDs = jogressEvoRootsFrameIDs;
+        BurstTamerFrameID = burstTamerFrameID;
+        AppFusionFrameIDs = appFusionFrameIDs;
+
+    }
+
+    public PlayCardAction(byte[] bytes)
+    {
+        CardIndex = -1;
+        TargetFrameID = -1;
+        JogressEvoRootsFrameIDs = null;
+        BurstTamerFrameID = -1;
+        AppFusionFrameIDs = null;
+
+        Deserialize(bytes);
+    }
+
+    public override void Execute(TurnStateMachine stateMachine)
+    {
+        stateMachine.SetPlayCard(CardIndex, TargetFrameID, JogressEvoRootsFrameIDs, BurstTamerFrameID, AppFusionFrameIDs);
+    }
+
+    public override void Deserialize(byte[] bytes)
+    {
+        int index = 0;
+
+        Protocol.Deserialize(out CardIndex, bytes, ref index);
+        Protocol.Deserialize(out TargetFrameID, bytes, ref index);
+
+        Protocol.Deserialize(out int JogressSize, bytes, ref index);
+        if (JogressSize > 0)
+        {
+            JogressEvoRootsFrameIDs = new int[JogressSize];
+            for (int i = 0; i < JogressSize; i++)
+            {
+                Protocol.Deserialize(out JogressEvoRootsFrameIDs[i], bytes, ref index);
+            }
+        }
+
+        Protocol.Deserialize(out BurstTamerFrameID, bytes, ref index);
+
+        Protocol.Deserialize(out int AppFuseSize, bytes, ref index);
+        if (AppFuseSize > 0)
+        {
+            AppFusionFrameIDs = new int[AppFuseSize];
+            for (int i = 0; i < AppFuseSize; i++)
+            {
+                Protocol.Deserialize(out AppFusionFrameIDs[i], bytes, ref index);
+            }
+        }
+    }
+
+
+    public override byte[] Serialize()
+    {
+        int JogressSize = JogressEvoRootsFrameIDs != null ? JogressEvoRootsFrameIDs.Length : 0;
+        int AppFuseSize = AppFusionFrameIDs != null ? AppFusionFrameIDs.Length : 0;
+
+        byte[] bytes = new byte[sizeof(int) * (5 + JogressSize + AppFuseSize)];
+        int index = 0;
+
+        Protocol.Serialize(CardIndex, bytes, ref index);
+        Protocol.Serialize(TargetFrameID, bytes, ref index);
+
+        Protocol.Serialize(JogressSize, bytes, ref index);
+        if (JogressSize > 0)
+        {
+            for (int i = 0; i < JogressSize; i++)
+            {
+                Protocol.Serialize(JogressEvoRootsFrameIDs[i], bytes, ref index);
+            }
+        }
+
+        Protocol.Serialize(BurstTamerFrameID, bytes, ref index);
+
+        Protocol.Serialize(AppFuseSize, bytes, ref index);
+        if (AppFuseSize > 0)
+        {
+            for (int i = 0; i < AppFuseSize; i++)
+            {
+                Protocol.Serialize(AppFusionFrameIDs[i], bytes, ref index);
+            }
+        }
+
+        return bytes;
+    }
+}

+ 45 - 0
Assets/Scripts/Script/Networking/GamePacketFactory.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+
+public static class GamePacketFactory
+{
+    static readonly Dictionary<byte, Func<byte[], IGamePacket>> Factories = new();
+    static readonly Dictionary<Type, byte> IdLookup = new();
+
+    static byte NextID = 0;
+
+    public static void Register<T>(Func<byte[], T> factory) where T : IGamePacket
+    {
+        Type type = typeof(T);
+
+        if (IdLookup.ContainsKey(type))
+        {
+            return;
+        }
+
+        byte id = NextID++;
+
+        IdLookup[type] = id;
+        Factories[id] = bytes => factory(bytes);
+    }
+
+    public static IGamePacket Create(byte id, byte[] bytes)
+    {
+        if (!Factories.TryGetValue(id, out var factory))
+        {
+            return null;
+        }
+
+        return factory(bytes);
+    }
+
+    public static byte GetId(Type type)
+    {
+        if (!IdLookup.TryGetValue(type, out byte id))
+        {
+            throw new InvalidOperationException($"{type.Name} is not a registered packet");
+        }
+
+        return id;
+    }
+}

+ 14 - 0
Assets/Scripts/Script/Networking/GamePacketRegistration.cs

@@ -0,0 +1,14 @@
+using UnityEngine;
+
+public static class PacketRegistration
+{
+    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
+    public static void RegisterAll()
+    {
+        GamePacketFactory.Register((btyes) => new AttackPermanentAction(btyes));
+        GamePacketFactory.Register((btyes) => new ActivateCardAction(btyes));
+        GamePacketFactory.Register((btyes) => new ActivatePermanentAction(btyes));
+        GamePacketFactory.Register((btyes) => new CheatAction(btyes));
+        GamePacketFactory.Register((btyes) => new PlayCardAction(btyes));
+    }
+}

+ 5 - 0
Assets/Scripts/Script/Networking/IGamePacket.cs

@@ -0,0 +1,5 @@
+public interface IGamePacket
+{
+    byte[] Serialize();
+    void Deserialize(byte[] bytes);
+}

+ 25 - 0
Assets/Scripts/Script/Player.cs

@@ -164,6 +164,31 @@ public class Player : MonoBehaviour
         SetHandCountText();
     }
 
+    #region Action Queue
+    Queue<MainPhaseAction> mainPhaseActions = new Queue<MainPhaseAction>();
+
+    public void QueueMainPhaseAction(MainPhaseAction action)
+    {
+        mainPhaseActions.Enqueue(action);
+    }
+
+    public MainPhaseAction DequeueMainPhaseAction()
+    {
+        if (mainPhaseActions.Count == 0)
+        {
+            return null;
+        }
+
+        return mainPhaseActions.Dequeue();
+    }
+
+    public bool HasMainPhaseAction()
+    {
+        return mainPhaseActions.Count > 0;
+    }
+
+    #endregion
+
     #region セキュリティアタックの座標
     public Vector3 SecurityAttackLocalCanvasPosition;
     #endregion

+ 74 - 24
Assets/Scripts/Script/TurnStateMachine.cs

@@ -995,6 +995,14 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                     yield break;
                 }
 
+                if (!GManager.instance.IsAI || gameContext.TurnPlayer.isYou)
+                {
+                    if (gameContext.TurnPlayer.HasMainPhaseAction())
+                    {
+                        gameContext.TurnPlayer.DequeueMainPhaseAction().Execute(this);
+                    }
+                }
+
                 #region AIモード
                 if (GManager.instance.IsAI && !gameContext.TurnPlayer.isYou)
                 {
@@ -1545,7 +1553,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                         }
                                         #endregion
 
-                                        photonView.RPC("SetActSkill", RpcTarget.All, fieldPermanentCard.ThisPermanent.TopCard.Owner.GetFieldPermanents().IndexOf(fieldPermanentCard.ThisPermanent), cardEffects1.IndexOf(cardEffect));
+                                        QueueMainPhaseAction(gameContext.TurnPlayer, new ActivatePermanentAction(fieldPermanentCard.ThisPermanent.TopCard.Owner.GetFieldPermanents().IndexOf(fieldPermanentCard.ThisPermanent), cardEffects1.IndexOf(cardEffect)));
                                     }
                                 }
                             }
@@ -1642,7 +1650,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
 
                                             if (doAttack)
                                             {
-                                                photonView.RPC("SetAttackingPermaent", RpcTarget.All, fieldPermanentCard.ThisPermanent.TopCard.Owner.GetFieldPermanents().IndexOf(fieldPermanentCard.ThisPermanent), -1);
+                                                QueueMainPhaseAction(gameContext.TurnPlayer, new AttackPermanentAction(fieldPermanentCard.ThisPermanent.TopCard.Owner.GetFieldPermanents().IndexOf(fieldPermanentCard.ThisPermanent), -1));
                                             }
 
                                             else
@@ -1736,7 +1744,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
 
                                         if (doAttack)
                                         {
-                                            photonView.RPC("SetAttackingPermaent", RpcTarget.All, fieldPermanentCard.ThisPermanent.TopCard.Owner.GetFieldPermanents().IndexOf(fieldPermanentCard.ThisPermanent), attackTargetID);
+                                            QueueMainPhaseAction(gameContext.TurnPlayer, new AttackPermanentAction(fieldPermanentCard.ThisPermanent.TopCard.Owner.GetFieldPermanents().IndexOf(fieldPermanentCard.ThisPermanent), attackTargetID));
                                         }
 
                                         else
@@ -1991,8 +1999,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                                     #endregion
 
                                                     //gameContext.NonTurnPlayer.LifeCardFrame.OffFrame_Select();
-
-                                                    photonView.RPC("SetAttackingPermaent", RpcTarget.All, gameContext.TurnPlayer.GetFieldPermanents().IndexOf(fieldPermanentCard2.ThisPermanent), gameContext.NonTurnPlayer.GetFieldPermanents().IndexOf(enemyFieldPermanentCard.ThisPermanent));
+                                                    QueueMainPhaseAction(gameContext.TurnPlayer, new AttackPermanentAction(gameContext.TurnPlayer.GetFieldPermanents().IndexOf(fieldPermanentCard2.ThisPermanent), gameContext.NonTurnPlayer.GetFieldPermanents().IndexOf(enemyFieldPermanentCard.ThisPermanent)));
                                                     return;
                                                 }
                                             }
@@ -2018,8 +2025,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                                     }
                                                 }
                                                 #endregion
-
-                                                photonView.RPC("SetAttackingPermaent", RpcTarget.All, gameContext.TurnPlayer.GetFieldPermanents().IndexOf(fieldPermanentCard2.ThisPermanent), -1);
+                                                QueueMainPhaseAction(gameContext.TurnPlayer, new AttackPermanentAction(gameContext.TurnPlayer.GetFieldPermanents().IndexOf(fieldPermanentCard2.ThisPermanent), -1));
                                                 return;
                                             }
                                         }
@@ -2437,7 +2443,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                                             IEnumerator _EndSelectCoroutine_SelectDigivolutionRoots(List<Permanent> permanents)
                                                             {
                                                                 yield return null;
-                                                                photonView.RPC("SetPlayCard", RpcTarget.All, handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[] { permanents[0].PermanentFrame.FrameID, permanents[1].PermanentFrame.FrameID }, -1, new int[0]);
+                                                                QueueMainPhaseAction(gameContext.TurnPlayer, new PlayCardAction(handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[] { permanents[0].PermanentFrame.FrameID, permanents[1].PermanentFrame.FrameID }, -1, new int[0]));
                                                             }
 
                                                             IEnumerator _NoSelectCoroutine()
@@ -2488,7 +2494,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                                             IEnumerator EndSelectCoroutine_SelectTamer(Permanent permanent)
                                                             {
                                                                 yield return null;
-                                                                photonView.RPC("SetPlayCard", RpcTarget.All, handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[0], permanent.PermanentFrame.FrameID, new int[0]);
+                                                                QueueMainPhaseAction(gameContext.TurnPlayer, new PlayCardAction(handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[0], permanent.PermanentFrame.FrameID, new int[0]));
                                                             }
 
                                                             IEnumerator _NoSelectCoroutine()
@@ -2539,8 +2545,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                                             {
                                                                 yield return null;
 
-                                                                //int sourceIndex = fieldCardFrame.GetFramePermanent().LinkedCards.IndexOf(cardSource);
-                                                                photonView.RPC("SetPlayCard", RpcTarget.All, handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[0], -1, new int[] { targetPermanent.PermanentFrame.FrameID, targetPermanent.LinkedCards.IndexOf(cardSource)});
+                                                                QueueMainPhaseAction(gameContext.TurnPlayer, new PlayCardAction(handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[0], -1, new int[] { targetPermanent.PermanentFrame.FrameID, targetPermanent.LinkedCards.IndexOf(cardSource) }));
                                                             }
 
                                                             IEnumerator _NoSelectCoroutine()
@@ -2557,7 +2562,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                                     #region usually evolves
                                                     void Digivolution()
                                                     {
-                                                        photonView.RPC("SetPlayCard", RpcTarget.All, handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[0], -1, new int[0]);
+                                                        QueueMainPhaseAction(gameContext.TurnPlayer, new PlayCardAction(handCard.cardSource.CardIndex, fieldCardFrame.FrameID, new int[0], -1, new int[0]));
                                                     }
                                                     #endregion
 
@@ -2610,7 +2615,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                             GManager.instance.You.playMatCardFrame.RemoveClickTarget();
                                             GManager.instance.You.playMatCardFrame.Frame.transform.parent.gameObject.SetActive(false);
 
-                                            photonView.RPC("SetPlayCard", RpcTarget.All, handCard.cardSource.CardIndex, handCard.cardSource.PreferredFrame().FrameID, new int[0], -1, new int[0]);
+                                            QueueMainPhaseAction(gameContext.TurnPlayer, new PlayCardAction(handCard.cardSource.CardIndex, handCard.cardSource.PreferredFrame().FrameID, new int[0], -1, new int[0]));
                                             selected = true;
 
                                             return;
@@ -2647,7 +2652,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
                                         GManager.instance.You.playMatCardFrame.RemoveClickTarget();
                                         GManager.instance.You.playMatCardFrame.Frame.transform.parent.gameObject.SetActive(false);
 
-                                        photonView.RPC("SetPlayCard", RpcTarget.All, handCard.cardSource.CardIndex, 0, new int[0], -1, new int[0]);
+                                        QueueMainPhaseAction(gameContext.TurnPlayer, new PlayCardAction(handCard.cardSource.CardIndex, 0, new int[0], -1, new int[0]));
                                         selected = true;
 
                                         return;
@@ -2945,7 +2950,7 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
 
                                         handCard.GetComponent<Draggable_HandCard>().CanPointerEnterExitAction = true;
 
-                                        photonView.RPC("SetActCardSkill", RpcTarget.All, handCard.cardSource.CardIndex, cardEffects1.IndexOf(cardEffect));
+                                        QueueMainPhaseAction(gameContext.TurnPlayer, new ActivateCardAction(handCard.cardSource.CardIndex, cardEffects1.IndexOf(cardEffect)));
                                     }
                                 }
                             }
@@ -3119,11 +3124,42 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
     }
     #endregion
 
-    #region Activation effect permanent determination
+    #region Queue Main Phase Action
+    public void QueueMainPhaseAction(Player player, MainPhaseAction action)
+    {
+        photonView.RPC("QueueMainPhaseAction_Internal", RpcTarget.All, player.PlayerID, GamePacketFactory.GetId(action.GetType()), action.Serialize());
+    }
+
     [PunRPC]
+    void QueueMainPhaseAction_Internal(int playerID, byte packetId, byte[] bytes)
+    {
+        Player player = GManager.instance.GetPlayerFromID(playerID);
+        
+        if (player == null)
+        {
+            return;
+        }
+
+        MainPhaseAction action = GamePacketFactory.Create(packetId, bytes) as MainPhaseAction;
+        if (action != null)
+        {
+            player.QueueMainPhaseAction(action);
+        }
+    }
+
+    #endregion
+
+    #region Activation effect permanent determination
     public void SetActSkill(int permanentIndex, int skillIndex)
     {
-        Permanent UseSkillPermanent = gameContext.TurnPlayer.GetFieldPermanents()[permanentIndex];
+        List<Permanent> feild = gameContext.TurnPlayer.GetFieldPermanents();
+
+        if (permanentIndex < 0 || permanentIndex >= feild.Count)
+        {
+            return;
+        }
+
+        Permanent UseSkillPermanent = feild[permanentIndex];
 
         if (0 <= skillIndex && skillIndex < UseSkillPermanent.EffectList(EffectTiming.OnDeclaration).Count)
         {
@@ -3133,9 +3169,13 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
     #endregion
 
     #region Activation effect card determination
-    [PunRPC]
     public void SetActCardSkill(int cardIndex, int skillIndex)
     {
+        if (cardIndex < 0 || cardIndex >= gameContext.ActiveCardList.Count)
+        {
+            return;
+        }
+
         CardSource UseSkillCard = gameContext.ActiveCardList[cardIndex];
 
         if (0 <= skillIndex && skillIndex < UseSkillCard.EffectList(EffectTiming.OnDeclaration).Count)
@@ -3146,9 +3186,13 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
     #endregion
 
     #region Play card decision
-    [PunRPC]
     public void SetPlayCard(int cardIndex, int TargetFrameID, int[] JogressEvoRootsFrameIDs, int BurstTamerFrameID, int[] AppFusionFrameIDs)
     {
+        if (cardIndex < 0 ||  cardIndex >= gameContext.ActiveCardList.Count)
+        {
+            return;
+        }
+
         PlayCard = gameContext.ActiveCardList[cardIndex];
         this.TargetFrameID = TargetFrameID;
 
@@ -3183,17 +3227,23 @@ public class TurnStateMachine : MonoBehaviourPunCallbacks
     #endregion
 
     #region Attack permanent determination
-    [PunRPC]
     public void SetAttackingPermaent(int permanentIndex, int attackTargetPermanentIndex)
     {
-        Permanent AttackingPermanent = gameContext.TurnPlayer.GetFieldPermanents()[permanentIndex];
+        List<Permanent> turnPlayerField = gameContext.TurnPlayer.GetFieldPermanents();
+        List<Permanent> nonTurnPlayerFeid = gameContext.NonTurnPlayer.GetFieldPermanents();
 
-        if (0 <= attackTargetPermanentIndex && attackTargetPermanentIndex < gameContext.NonTurnPlayer.GetFieldPermanents().Count)
+        AttackingPermanent = null;
+        DefendingPermanent = null;
+
+        if (permanentIndex >= 0 && permanentIndex < turnPlayerField.Count)
         {
-            this.DefendingPermanent = gameContext.NonTurnPlayer.GetFieldPermanents()[attackTargetPermanentIndex];
+            AttackingPermanent = turnPlayerField[permanentIndex];
         }
 
-        this.AttackingPermanent = AttackingPermanent;
+        if (attackTargetPermanentIndex >= 0 && attackTargetPermanentIndex < nonTurnPlayerFeid.Count)
+        {
+            DefendingPermanent = nonTurnPlayerFeid[attackTargetPermanentIndex];
+        }        
     }
     #endregion