Related Posts Plugin for WordPress, Blogger...

6-15 Using Handler To Respond Requisition In Photon Server

本章繼續完成Server端處理Request的部分,基本概念與Client端差不多,只不過在Server時將用詞轉換成Handler。由於本章已經預先完成許多類別了,如果有直接跳到本章節進來看的朋友,建議先從6-1開始看比較好唷。

首先定義BaseHandler,一樣要有OperationCode,然後定義抽象方法OnOperationRequest。

接著撰寫LoginHandler去處理Client端傳來的Login Request,在建構子中先定義opercationCode為Login。

接著就可以覆寫OnOperationRequest,然後再方法內實作與資料庫驗證帳號密碼的動作。

使用我們前幾章寫好的UserManager,內部會處理跟User有關的資料庫操作。然後要特別注意的是,這邊我們使用了ReturnCode,這是由Photon定義的可供傳送小資訊(short)的參數,畢竟不是每一次Responce都需要建立一個Dictionary回傳吧。好比這次處理登入的事件,只需回傳Success或Failed即可。

接著,雖然ReturnCode是short型別,建議還是弄個enum去管理。
接著在NoliahFantasyServer.cs的主類別中新增LoginHandler,並將之Add進Dictionary中進行管理。

在MyClientPeer.cs中的OnOperationRequest中便能夠直接呼叫,先從operationRequest.OperationCode中取得byte碼,再從NoliahFantasyServer的Handler Dictionary中取得對應的BaseHandler,呼叫BaseHandler內的OnOperationRequest進行處理。

底下提供Server端本次修改的完整原始碼。
NoliahFantasyServer.cs:

using System;
using System.IO;
using System.Collections.Generic;
using Photon.SocketServer;
using ExitGames.Logging;
using ExitGames.Logging.Log4Net;
using log4net.Config;
using NHibernate;
using NHibernate.Cfg;
using NoliahFantasyServer.Constant;
using NoliahFantasyServer.Handler;
using NoliahFantasyServer.Model;
using NoliahFantasyServer.Manager;

namespace NoliahFantasyServer
{
    public class NoliahFantasyServer : ApplicationBase
    {

        public static readonly ILogger logger = LogManager.GetCurrentClassLogger();
        public static Dictionary handlerDict =
            new Dictionary();

        // 當Client端發出Request的時候
        protected override PeerBase CreatePeer(InitRequest initRequest)
        {
            return new MyClientPeer(initRequest);
        }

        // Server端啟動的時候初始化
        protected override void Setup()
        {
            InitLogger();
            InitHandler();
        }

        // Server端關閉的時候
        protected override void TearDown()
        {
        }

        void InitLogger()
        {
            // 日誌初始化
            log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = 
                Path.Combine(this.ApplicationRootPath, "bin_Win64", "log");
            FileInfo loggerConfig = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));
            if (loggerConfig.Exists)
            {
                // 設置使用log4net的Log功能
                LogManager.SetLoggerFactory(ExitGames.Logging.Log4Net.Log4NetLoggerFactory.Instance);
                // 讓log4net讀取config
                XmlConfigurator.ConfigureAndWatch(loggerConfig);
            }
            logger.Info("Setup Log4Net Compeleted!");
        }

        void InitHandler(){
            LoginHandler loginHandler = new LoginHandler();
            handlerDict.Add(loginHandler.operationCode, loginHandler);
        }

    }
}


BaseHandler.cs:

using System;
using Photon.SocketServer;
using NoliahFantasyServer.Constant;

namespace NoliahFantasyServer.Handler
{
    public abstract class BaseHandler
    {
        public OperationCode operationCode;
        public abstract void OnOperationRequest(OperationRequest operationRequest,
                                                   SendParameters sendParameters,
                                                ClientPeer clientPeer);
    }
}


LoginHandler.cs:

using System;
using Photon.SocketServer;
using NoliahFantasyServer.Constant;
using NoliahFantasyServer.Manager;

namespace NoliahFantasyServer.Handler
{
    public class LoginHandler : BaseHandler
    {
        public LoginHandler(){
            operationCode = OperationCode.Login;
        }

        public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer clientPeer)
        {
            // 從OperationRequest取得帳號密碼資訊
            object account;
            operationRequest.Parameters.TryGetValue((byte)ParameterCode.Acccount, out account);
            object password;
            operationRequest.Parameters.TryGetValue((byte)ParameterCode.Password, out password);

            // 使用UserManager向資料庫驗證使用者的帳號密碼
            UserManager userManager = new UserManager();
            bool isVerify = userManager.VerifyUser(account as string, password as string);

            // 使用short類型的ReturnCode簡單回傳結果
            OperationResponse operationResponse = new OperationResponse((byte)OperationCode.Login);
            if(isVerify){
                operationResponse.ReturnCode = (short)ReturnCode.Success;
            }else{
                operationResponse.ReturnCode = (short)ReturnCode.Failed;
            }
            // 發送Response
            clientPeer.SendOperationResponse(operationResponse, sendParameters);
        }
    }
}


MyClientPeer.cs:

using System;
using System.Collections;
using System.Collections.Generic;
using Photon.SocketServer;
using PhotonHostRuntimeInterfaces;
using NoliahFantasyServer.Handler;
using NoliahFantasyServer.Constant;

namespace NoliahFantasyServer
{
    public class MyClientPeer : ClientPeer
    {
        const int SIGNUP = 1;
        const int BROADCAST = 2;
        public MyClientPeer(InitRequest initRequest) : base(initRequest)
        {
        }

        // Client端斷開連結的時候
        protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
        {
        }

        // 處理Client的Request請求
        protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
        {
            BaseHandler baseHandler = null;
            NoliahFantasyServer.handlerDict.TryGetValue((OperationCode)operationRequest.OperationCode, out baseHandler);
            baseHandler.OnOperationRequest(operationRequest, sendParameters, this);
        }

        
    }
}


ReturnCode.cs:

using System;
namespace NoliahFantasyServer.Constant
{
    public enum ReturnCode : short
    {
        Success,
        Failed
    }
}


接下來繼續修改Client端,處理從Server傳回的Response。由於登入部分的處理涉及『場景切換』以及『顯示提示訊息』這兩個與UI比較相關的工作,所以我將這部分的處理劃分到LoginWindow.cs中。

接著在LoginWindow.cs中處理OnLoginResponse,失敗的話應有提示訊息顯示『帳號或密碼錯誤』,成功的話應要跳轉場景,但本章就先顯示個『登入成功』就好了。

以下提供原始碼:
LoginRequest.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ExitGames.Client.Photon;

public class LoginRequest : Request {

 [HideInInspector] public string account;
 [HideInInspector] public string password;

 LoginWindow loginWindow;
 void Start(){
  base.Start ();
  loginWindow = GetComponent ();
 }

 public override void OnDefaultRequest ()
 {
  Dictionary data = new Dictionary ();
  data.Add((byte)ParameterCode.Acccount, account);
  data.Add((byte)ParameterCode.Password, password);
  // 向Server發出Request,true代表建立可靠連接
  PhotonEngine.Instance.GetPeer().OpCustom((byte)operationCode, data, true);
 }

 public override void OnOperationResponse (OperationResponse operationResponse)
 {
  ReturnCode returnCode = (ReturnCode)operationResponse.ReturnCode;
  loginWindow.OnLoginResponse (returnCode);

  print (operationResponse.ReturnCode);
 }
}


LoginWindow.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class LoginWindow : Panel {

 [SerializeField] SignupWindow signupWindow;
 [SerializeField] InputField accountInput;
 [SerializeField] InputField passwordInput;
 [SerializeField] Text hintText;

 LoginRequest loginRequest;

 void Start(){
  base.Start ();
  loginRequest = GetComponent ();
 }

 public void OnLoginButton(){
  hintText.text = string.Empty;
  // 設定帳號及密碼參數後,呼叫OnDefaultRequest發送Request給Photon
  loginRequest.account = accountInput.text;
  loginRequest.password = passwordInput.text;
  loginRequest.OnDefaultRequest ();
 }

 public void OnShowSignupButton(){
  this.Hide ();
  signupWindow.Show ();
 }

 public void OnLoginResponse(ReturnCode returnCode){
  if (returnCode == ReturnCode.Success) {
   hintText.text = "登入成功";
  } else {
   hintText.text = "帳號或密碼錯誤";
  }
 }
}


接著來測試遊戲吧,首先輸入錯誤的帳號密碼。


再來輸入正確的帳號密碼,嗯,成功了!


留言