简体   繁体   English

如何使用SignalR将客户端加入MVC组

[英]How to join a client to group in MVC using signalR

I looked everywhere but could not find or understand how to make a client join a signalr group. 我四处张望,但找不到或不知道如何使客户加入信号员小组。 I'm using mvc 4 with the system users. 我正在与系统用户一起使用mvc 4。 Before I show you my code, let me explain whan I'm trying to do: I am programming a game of online Tic-TacToe. 在向您展示我的代码之前,让我解释一下我想做的事情:我正在编写在线Tic-TacToe游戏。 My goal is to that anytime a user will open a new game, a new grop(for example: "GameRoom{Game id}" will be created. now whenever a new player will join the game, he will also join the game group. Then, when one of the players will make a move, the other player browser will refresh to show the new game board. I programed it in the host side, but don't know how to do it at the client side. And one last thing - my current code currently using timer to get the data. The point is to replace it with signalR. Here's my code: 我的目标是,只要用户打开新游戏,就会创建新的grop(例如:“ GameRoom {Game id}”)。现在,只要有新玩家加入游戏,他都会加入游戏组。然后,当其中一个播放器移动时,另一个播放器浏览器将刷新以显示新游戏板,我在主机端进行了编程,但不知道如何在客户端进行。东西-我当前使用定时器获取数据的当前代码。关键是用signalR替换它。这是我的代码:

    namespace TicTacToeMVCPart2.Hubs
{


     public class GamesHub : Hub
    {


        public Task JoinGameRoom(string GameRoomName)
        {
            return Groups.Add(Context.ConnectionId, GameRoomName);
        }

        public void PlayerClick(string roomName)
        {
            Clients.OthersInGroup(roomName).OnSquareClicked();
        }
    }
}

    namespace TicTacToeMVCPart2.Models
{


     // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
        public class ApplicationUser : IdentityUser
    {
        public ApplicationUser()
        {
            Games = new List<Game>();    
        }
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }


        public List<Game> Games { get; set; }

        public int GamesWon { get; set; }

        public int GamesLost { get; set; }


        public int GamesDraw { get; set; }


    }

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }

        public DbSet<Game> Games { get; set; }



        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }
}

    namespace TicTacToeMVCPart2.Models
{

    public class Game
    {

        public Game()
        {
        }

        public Game(string user1Id)
        {
            User1Id = user1Id;
        }
        public int GameId { get; set; }

        [Required]
        public string User1Id { get; set; }

        public string User2Id { get; set; }

        public string UserIdTurn { get; set; }

        public string WinnerId { get; set; }

        public bool IsGameOver { get; set; }

        public SquareState Square1 { get; set; }

        public SquareState Square2 { get; set; }

        public SquareState Square3 { get; set; }

        public SquareState Square4 { get; set; }

        public SquareState Square5 { get; set; }

        public SquareState Square6 { get; set; }

        public SquareState Square7 { get; set; }

        public SquareState Square8 { get; set; }

        public SquareState Square9 { get; set; }
    }
    }

    namespace TicTacToeMVCPart2.ViewModels

       {

    public class GameModel
    {
        public Game Game { get; set; }

        public ApplicationUser User { get; set; }
    }
    }

namespace TicTacToeMVCPart2.Controllers
{
   public class GamesController : Controller
{
   ApplicationDbContext context = new ApplicationDbContext();

   private ApplicationUserManager _userManager;

   public ApplicationUserManager UserManager
   {
       get
       {
           return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
       }
       private set
       {
           _userManager = value;
       }
   }

    /// <summary>
    /// Returns the Logged User Id - defult 0
    /// </summary>
    public int LoggedUserId
    {
        get { return Utilites.LoggedUserId(Request); }
    }

           public ActionResult MyStats()
    {
        return View("MyStats", UserManager.FindById(User.Identity.GetUserId()));
    }

    /// <summary>
    ///  shows all Open Games of other users the logged in user can join
    /// </summary>
    /// <returns></returns>
    public PartialViewResult OpenGames()
    {
        string userId = User.Identity.GetUserId();
        var results = (from game in context.Games
                       where game.User2Id == null && game.User1Id != userId
                       select game).ToList();


        return PartialView("_allOpenGames", results);
    }



    /// <summary>
    ///  returns  "_openGameShowScreen" Partial View for a specific game
    /// </summary>
    /// <param name="gameId"></param>
    /// <returns></returns>
    public PartialViewResult OpenGameShowScreen(int gameId)
    {


        var gameResult = GetGameById(gameId);


        ApplicationUser user = new ApplicationUser();

        if (gameResult != null)
        {
            user = UserManager.FindById(gameResult.User1Id);
        }

        GameModel model = new GameModel() { Game = gameResult, User = user };

        return PartialView("_openGameShowScreen", model);
    }

    /// <summary>
    /// method that allows users join games and returns the game view or message view for errors
    /// </summary>
    /// <param name="gameId"></param>
    /// <returns></returns>
    public ActionResult UserJoinGame(int gameId)
    {

        ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());


        if (user == null)
        {
            Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");
            return View("Message");
        }
        else
        {
            Game gameResult = GetGameById(gameId);

            if (gameResult.User2Id != null) //game already taken
            {
                Utilites.CreateMsgCookie(Response, "Error", "Game already being taken");
                return View("Message");
            }
            else
            {
                gameResult.User2Id = user.Id;

                Random tmpRNG = new Random();
                int tmpInt = tmpRNG.Next(2);
                if (tmpInt == 0)
                {
                    gameResult.UserIdTurn = gameResult.User1Id;
                }
                else
                {
                    gameResult.UserIdTurn = gameResult.User2Id;
                }
                user.Games.Add(gameResult);
                context.SaveChanges();

                GameModel model = new GameModel()
                {
                    User = user,
                    Game = gameResult
                };

                return View("GameScreen", model);

            }
        }
    }

    /// <summary>
    /// return "ActiveGamesScreen" view with the results of ActiveGameShowResults(user) as the model
    /// </summary>
    /// <param name="userId"></param>
    /// <returns></returns>
    public ActionResult ActiveGames()
    {

        ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());



        if (user == null)
        {
            Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");
            return View("Message");
        }
        else
        {
            List<Game> activeGames = ActiveGameShowResults(user);
                //ActiveGameShowResults(user);
            return View("ActiveGamesScreen", activeGames);


        }
    }

    /// <summary>
    /// return all active games of a specific user
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    private List<Game> ActiveGameShowResults(ApplicationUser user)
    {

        List<Game> results = new List<Game>();

        if (user != null)
        {
            results = context.Games.Where(x => x.IsGameOver == false && x.User2Id != null &&
                (x.User1Id == user.Id || x.User2Id == user.Id)).ToList();
        }

        return results;

    }

    /// <summary>
    /// returns "_activeGameShowScreen" Partial View for a specific game or error in View "Message"
    /// </summary>
    /// <param name="gameId"></param>
    /// <returns></returns>
    public ActionResult ActiveGameShowScreen(int gameId)
    {
        Game game = GetGameById(gameId);

        if (game == null)
        {
            Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");
            return View("Message");
        }
        else
        {
            string userId = User.Identity.GetUserId();
            //Get rival user Id
            if (game.User1Id == userId) 
            {
                userId = game.User2Id;
            }

            else
            {
                userId = game.User1Id;
            }

            ApplicationUser user = UserManager.FindById(userId);

            GameModel model = new GameModel() { Game = game, User = user };


            return PartialView("_activeGameShowScreen", model);
        }
    }


    /// <summary>
    /// get game from context by gameId , Defult result - null
    /// </summary>
    /// <param name="gameId"></param>
    /// <returns></returns>
    private Game GetGameById(int gameId)
    {
        Game gameResult = (from game in context.Games
                           where game.GameId == gameId
                           select game).FirstOrDefault();
        return gameResult;
    }

    /// <summary>
    /// method to create new gamrs, returns "GameScreen"  View or error by message view
    /// </summary>
    /// <returns></returns>
    public ViewResult CreateNewGame()
    {
        var user = UserManager.FindById(User.Identity.GetUserId());

        if (user == null)
        {
            Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");
            return View("Message");
        }
        else
        {

            Game game = new Game();
            game.User1Id = user.Id;
            user.Games.Add(game);
            context.Games.Add(game);
            context.SaveChanges();

            GameModel model = new GameModel { Game = game, User = user };

            return View("GameScreen", model);
        }
    }

    /// <summary>
    /// returns GameScreen View by gameId or error by message view 
    /// </summary>
    /// <param name="gameId"></param>
    /// <returns></returns>
    public ViewResult GoToGameScreen(int gameId)
    {
        var user = UserManager.FindById(User.Identity.GetUserId());

        if (user == null)
        {
            Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");
            return View("Message");
        }
        else
        {

            Game game = GetGameById(gameId);

            GameModel model = new GameModel { Game = game, User = user };

            return View("GameScreen", model);
        }
    }


}

} }

and here's client side related code: 这是客户端相关的代码:

namespace TicTacToeMVCPart2.Controllers
{
    public class GamesApiController : ApiController
{
    ApplicationDbContext context = new ApplicationDbContext();

    private ApplicationUserManager _userManager;

    public IEnumerable<ApplicationUser> Get()
    {
        return context.Users;
    }


    public ApplicationUserManager UserManager
    {

        get
        {
            //(System.Web.HttpContext.Current)//lock (System.Web.HttpContext.Current)
            //{
                return _userManager ?? System.Web.HttpContext.Current.Request.GetOwinContext().GetUserManager<ApplicationUserManager>();                    
            //}
        }
        private set
        {
            _userManager = value;
        }
    }



    #region Methods
    /// <summary>
    /// update the server data by reciving the model and square and returns the new model
    /// </summary>
    /// <param name="_model"></param>
    /// <param name="squareId"></param>
    /// <returns></returns>

    //square clicked via post
    [Route("api/gamesapi/{squareId}")]
    public HttpResponseMessage Post([FromBody]GameModel model, int squareId)
    {
        HttpResponseMessage response;

        if (model == null)
        {
            //Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");

            response = Request.CreateErrorResponse(HttpStatusCode.NotFound, "model wasn't found");

            return response;
        }

        //GameModel model = JsonConvert.DeserializeObject<GameModel>(_model);

        Game game = GetGameById(model.Game.GameId);

        if (game == null)
        {
            response = Request.CreateErrorResponse(HttpStatusCode.NotFound, "game wasn't found");
        }
        else
        {

            if (game.UserIdTurn == game.User1Id) //pressing user is user1
            {
                ChangeSquareState(game, squareId, true);
                game.UserIdTurn = game.User2Id;

            }
            else //game.UserIdTurn == game.User2Id - pressing user is user2
            {
                ChangeSquareState(game, squareId, false);
                game.UserIdTurn = game.User1Id;

            }

            SquareState[] board = new SquareState[] {game.Square1,game.Square2,game.Square3,game.Square4,
                                                        game.Square5,game.Square6,game.Square7,game.Square8,game.Square9};

            if (didPlayerWin(board))
            {
                game.WinnerId = model.User.Id;
                UpdateUserGameState(1, game.User1Id);
                UpdateUserGameState(2, game.User2Id);
                game.IsGameOver = true;
            }
            else
            {
                bool isBoardFull = true;
                for (int i = 0; i < board.Length; i++)
                {
                    if (board[i] == SquareState.Blank)
                    {
                        isBoardFull = false;
                        break;
                    }
                }
                if (isBoardFull)
                {
                    UpdateUserGameState(3, game.User1Id);
                    UpdateUserGameState(3, game.User2Id);
                    game.IsGameOver = true;
                }
            }

            context.SaveChanges();

            response = Request.CreateResponse(HttpStatusCode.OK, game);

        }
        return response;
    }

    /// <summary>
    /// When a game is over, recive a gameState and update the user. 1 for a win, 2 for loss, 3 for aa draw
    /// </summary>
    /// <param name="gameState"></param>
    private void UpdateUserGameState(int gameState, string userId)
    {
        var user = UserManager.FindById(userId);
        switch (gameState)
        {
            case 1:

                user.GamesWon++;
                break;

            case 2:
                user.GamesLost++;
                break;

            case 3:
                user.GamesDraw++;
                break;
            default:
                break;

        }

        UserManager.UpdateAsync(user);

    }

    [HttpGet]
    [Route("api/gamesapi/{gameId}")]
    /// <summary>
    /// method to bring the latest game's state from the context and send it back in a GameModel
    /// </summary>
    /// <param name="_model"></param>
    /// <returns></returns>
    public HttpResponseMessage Get(int gameId)
    {
    }

    /// <summary>
    /// method that check if the board have a line(3 squars in a row)
    /// of the same  element , defult - returns fault
    /// </summary>
    /// <param name="board"></param>
    /// <returns></returns>
    private bool didPlayerWin(SquareState[] board)
    {
    }

    /// <summary>
    /// change the SquareState of a specific square of the sended game according to the pressing user
    /// </summary>
    /// <param name="game"></param>
    /// <param name="SquareId"></param>
    /// <param name="_isUser1"></param>

    private void ChangeSquareState(Game game, int SquareId, bool _isUser1)
    {
    }

    /// <summary>
    /// get game from context by gameId , Defult result - null
    /// </summary>
    /// <param name="gameId"></param>
    /// <returns></returns>
    private Game GetGameById(int gameId)
    {
        Game gameResult = (from game in context.Games
                           where game.GameId == gameId
                           select game).FirstOrDefault();
        return gameResult;
    } 
    #endregion
}

} }

GameScreen.cshtml: GameScreen.cshtml:

  @model TicTacToeMVCPart2.ViewModels.GameModel @{ ViewBag.Title = "GameScreen"; Layout = "~/Views/Shared/_Layout.cshtml"; } <script> var game_model = @Html.Raw(Json.Encode(@Model)); </script> <h2>GameScreen</h2> <div id="waiting"> @if (Model.Game.User2Id == null) { <h4 id="waitingMsg">Waiting for another player to join the game</h4> } </div> <div id="TurnTitle"></div> <br /> <div id="gameBoard"> </div> @section Scripts { <script src="~/Scripts/TicTacToeScript.js"></script> } 

 var tableDiv; var board; var timer; var hub; var con; function UpdateTurnTitle() { var div = document.getElementById("TurnTitle"); if (div) { if (game_model.Game.UserIdTurn === game_model.User.Id) { div.innerHTML = "Your Turn"; } else { div.innerHTML = "opponent Turn"; } } } function convertGameStateValueToEnum(val) { // function that helps understand the class names var res; switch (val) { case 0: res = "Blank"; break; case 1: res = "User1"; break; case 2: res = "User2"; break; } return res; } $(document).ready( (function () { con = $.hubConnection(); hub = con.createHubProxy('GamesHub'); hub.on('OnSquareClicked', RefreshGame()); con.start(); if (game_model.Game.User2Id != null) { UpdateTurnTitle(); } tableDiv = document.getElementById("gameBoard"); FillArray(); if (tableDiv) { // creating the Tic-Tac-Toe table for the first time the page is loaded var counter = 1; for (var i = 0; i < 3; i++) { var rowDiv = document.createElement("div"); rowDiv.className = "row"; for (var j = 0; j < 3; j++) { var colDiv = document.createElement("div"); colDiv.id = counter; var partOfClassName = convertGameStateValueToEnum(board[counter - 1]); colDiv.className = 'col-sm-4 TicTac-block ' + partOfClassName; if (partOfClassName == 'Blank') { // add Event Listener for blank squars colDiv.addEventListener("click", click, false); } counter++; rowDiv.appendChild(colDiv); } tableDiv.appendChild(rowDiv); } timer = setInterval(function () { RefreshGame(); }, 1000); } }()) ); function RefreshTable() { FillArray(); for (var i = 0; i < board.length; i++) { //var div = $('#' + (i + 1)); var div = document.getElementById((i + 1).toString()); var partOfClassName = convertGameStateValueToEnum(board[i]); div.className = 'col-sm-4 TicTac-block ' + partOfClassName; if (partOfClassName != 'Blank') { div.removeEventListener("click", click, false); } } } function FillArray() { //filling the board by using game_model.Game Squares. should be done after refreshing data and before // RefreshTable functions board = [ game_model.Game.Square1, game_model.Game.Square2, game_model.Game.Square3, game_model.Game.Square4, game_model.Game.Square5, game_model.Game.Square6, game_model.Game.Square7, game_model.Game.Square8, game_model.Game.Square9 ]; } function click() { // happends when one square of the div board has been clicked if (game_model.Game.User2Id == 0) { alert("Waiting for another player to join the game"); return; } if (game_model.Game.UserIdTurn != game_model.User.Id) { alert("It's not your turn yet"); return; } var div = document.getElementById(this.id); RefreshGameAfterClick(div); } function RefreshGame() { //timer function RefreshData(); if (game_model.Game.User2Id != null) { var divChild = document.getElementById('waitingMsg'); //remove waitingMsg div if exist when there are two players if (divChild) { var divFather = document.getElementById('waiting'); divFather.removeChild(divChild); } RefreshTable(); if (game_model.Game.IsGameOver) { GameOver(); } else { UpdateTurnTitle(); } } } // commiting GameOver functions function GameOver() { clearInterval(timer); //updating Title by checking the results if (game_model.Game.WinnerId == null) { var divTitle = document.getElementById('TurnTitle'); if (divTitle) { divTitle.innerHTML = 'Game Over - Draw'; } } else if (game_model.Game.WinnerId == game_model.User.Id) { var divTitle = document.getElementById('TurnTitle'); if (divTitle) { divTitle.innerHTML = 'Game Over - You won'; } } else { var divTitle = document.getElementById('TurnTitle'); if (divTitle) { divTitle.innerHTML = 'Game Over - You Lost'; } } DisableAllClicks(); } function DisableAllClicks() // disabling all the Event Listeners of the game board divs { for (var i = 0; i < board.length; i++) { var div = document.getElementById((i + 1).toString()); div.removeEventListener("click", click, false); } } function RefreshGameAfterClick(div) { RefreshDataAfterClick(div); if (game_model.Game.IsGameOver) { // if game over, active GameOver method GameOver(); } else { UpdateTurnTitle(); } } function RefreshDataAfterClick(div) { // sends post to server and updating game_model.Game variable $.ajax({ type: "POST", url: '/api/GamesApi/' + div.id, dataType: 'json', contentType: 'application/json; charset=utf-8', data: JSON.stringify(game_model), success: function (data) { if (data) { game_model.Game = data; } } }) } function RefreshData() { // timer function - GET type $.ajax({ type: "GET", url: '/api/GamesApi/' + game_model.Game.GameId, dataType: 'json', contentType: 'application/json; charset=utf-8', success: function (data) { if (data) { game_model.Game =data; } } }) } 

This is a must read before you start. 这是开始之前必须阅读的内容。 I am sure you've seen it already... http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups 我相信您已经看过了... http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups

If you have problems figuring out how to call hub method I would suggest reading this one too: http://www.asp.net/signalr/overview/getting-started/introduction-to-signalr 如果您在弄清楚如何调用集线器方法时遇到问题,建议您也阅读此方法: http : //www.asp.net/signalr/overview/getting-started/introduction-to-signalr

Solution: 解:

Basically in your hub method (c#) 基本上在您的集线器方法中(C#)

public Task JoinGameRoom(string GameRoomName)
{
    return Groups.Add(Context.ConnectionId, GameRoomName);
}

In javascript: 在javascript中:

var gameHub = $.connection.GameHub;
// You will have to start connection before joining
$.connection.hub.start();
gameHub.server.joinGameRoom("room1");

For joining room right after connected: 连接后立即进入房间:

$.connection.hub.start().done( function () {
    gameHub.server.joinGameRoom("room1");
});

You would need to grab the hub context from the GlobalHost and then add the user to the group. 您需要从GlobalHost获取中心环境,然后将用户添加到组中。 You also get an opportunity to add in OnConnected and to leave OnDisconnected . 您还将有机会添加OnConnected并保留OnDisconnected

public void JoinToGroup(string groupToJoin, string connectionId)
{
    var context = GlobalHost.ConnectionManager.GetHubContext<GamesHub>();
    context.Groups.Add(connectionId, groupToJoin);      
}

I found the problem. 我发现了问题。 my javascript had(I just also noticed the javascript I wrote here was a previous version): 我的javascript拥有(我也注意到我在这里编写的javascript是以前的版本):

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> var hub = $.Connection.gamesHub; 

and the right code is $.connection.gamesHub with small case c. 正确的代码是带有小写字母c的$ .connection.gamesHub。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM