Sign In Register

How can we help you today?

Start a new topic
Answered

Simultaneous turn based multiplayer game with sequencing point

Hello I an new to GameSparks and I have some questions on my mind hoping that someone can help me.

 I am using unity and I create a challenge where both users that have joined a challenge can send their inputs in parallel.

How can I detect that another user has joined the challenge ?. I can't find any api/callback for this in GS Unity Sdk. Do I need to pool the state of the challenge on each connected player?

How can I detect that after both users have sent their inputs ,the server can close the challenge with a winner?

Also how can the clients be notified that the game has ended and display "Winner"/"Looser" message?


(I am learning gamesparks api and I an grateful for any comment/question about my question)


Best Answer

If you create a private challenge you must invite another player before the challenge begins. You can set a minimum number of players needed to start a challenge according to the CreateChallengeRequest.


In Unity the string would look something like this:

  

new CreateChallengeRequest().SetChallengeShortCode("ticTacToe").SetUsersToChallenge(userIds).SetMinPlayers(2).SetEndTime(System.DateTime.Today.AddDays(1)).SetChallengeMessage("Shane has challenged you to a game of Tic Tac Toe!").Send((response) =>
		{
			if (response.HasErrors)
			{
				Debug.Log(response.Errors.ToString());
			}
			else
			{
				//Show message saying sent!
			}
		});

  That would then send a message to all players who are online currently or show one when they log on next. To get a list of of games that you have been invited to you can do something like:

    

new ListChallengeRequest().SetShortCode("ticTacToe").SetState("RECEIVED").SetEntryCount(50).Send((response) =>
		{
			foreach (var challenge in response.ChallengeInstances)
			{
				string challengeId = challenge.ChallengeId;
			}

		});

 When both players accept the challenge effectively starts and changes it's state to "RUNNING".


To check both users inputs you can use Cloud Code.


On the Cloud Code page, click "Global Messages" and find "ChallengeStartedMessage". This code will be called when a new challenge starts, ie. when both players accept it.


Here we can declare variables for each challenge instance so if you do the following:


 

//This gets the challengeInstanceId
var challenge = Spark.getChallenge(Spark.data.challenge.challengeId);

//This creates our variable for the challenge, you can create as many as you want.
var playerOneScore = 0;
var playerTwoScore = 0;

//We then set our Script Data for the challenge with a key of "playerOneScore" and our variable as the data to send.
challenge.setScriptData("playerOneScore", playerOneScore);
challenge.setScriptData("playerTwoScore", playerTwoScore);
//This data will be stored in the database for this challenge's instance

 


Then you can create an event called "TakeTurn" and set it up like the following:


The big difference here is that you change the "Default Calc" to "Used In Script"


Next we need to make our challenge:


Things to note here are that we want a turn based challenge, and the "Take Turn" event we created a moment ago will be the event that signals a player has taken their turn.


Instead of using a Leaderboard we are going to use a "Scripted Outcome"


Finally we are going to go back to Cloud Code.


Under challenge events you'll see the event you created "Take Turn"


Click that and add something like:

   

//Get our challenge instance Id
var challenge = Spark.getChallenge(Spark.data.challengeInstanceId);

//Create a new variable that will pull in our scores from "ScriptData" which we set in the ChallengeStartedMessage
var playerOneScore = challenge.getScriptData("playerOneScore");
var playerTwoScore = challenge.getScriptData("playerTwoScore");

if(playerOneScore > playerTwoScore)
{
    challenge.winChallenge("Player 1 User Id");
    challenge.setScriptData("whoWon", "Player 1 User Id);
}
else(playerOneScore < playerTwoScore)
{
    challenge.setScriptData("whoWon", "Player 2 User Id);
}
else
{
    challenge.drawChallenge();
} 



You can check the status of a challenge with GetChallengeRequest, which in Unity looks like:


 

new GetChallengeRequest().SetChallengeInstanceId("challengeInstanceId").Send((response) =>
		{
			string userWhoWon = response.Challenge.ScriptData.GetString("whoWon");
		});

 

The code above is purely for reference and you can find out more about the Cloud Code API here: 

https://docs.gamesparks.net/documentation/gamesparks-service-cloud-code-api


Answer

If you create a private challenge you must invite another player before the challenge begins. You can set a minimum number of players needed to start a challenge according to the CreateChallengeRequest.


In Unity the string would look something like this:

  

new CreateChallengeRequest().SetChallengeShortCode("ticTacToe").SetUsersToChallenge(userIds).SetMinPlayers(2).SetEndTime(System.DateTime.Today.AddDays(1)).SetChallengeMessage("Shane has challenged you to a game of Tic Tac Toe!").Send((response) =>
		{
			if (response.HasErrors)
			{
				Debug.Log(response.Errors.ToString());
			}
			else
			{
				//Show message saying sent!
			}
		});

  That would then send a message to all players who are online currently or show one when they log on next. To get a list of of games that you have been invited to you can do something like:

    

new ListChallengeRequest().SetShortCode("ticTacToe").SetState("RECEIVED").SetEntryCount(50).Send((response) =>
		{
			foreach (var challenge in response.ChallengeInstances)
			{
				string challengeId = challenge.ChallengeId;
			}

		});

 When both players accept the challenge effectively starts and changes it's state to "RUNNING".


To check both users inputs you can use Cloud Code.


On the Cloud Code page, click "Global Messages" and find "ChallengeStartedMessage". This code will be called when a new challenge starts, ie. when both players accept it.


Here we can declare variables for each challenge instance so if you do the following:


 

//This gets the challengeInstanceId
var challenge = Spark.getChallenge(Spark.data.challenge.challengeId);

//This creates our variable for the challenge, you can create as many as you want.
var playerOneScore = 0;
var playerTwoScore = 0;

//We then set our Script Data for the challenge with a key of "playerOneScore" and our variable as the data to send.
challenge.setScriptData("playerOneScore", playerOneScore);
challenge.setScriptData("playerTwoScore", playerTwoScore);
//This data will be stored in the database for this challenge's instance

 


Then you can create an event called "TakeTurn" and set it up like the following:


The big difference here is that you change the "Default Calc" to "Used In Script"


Next we need to make our challenge:


Things to note here are that we want a turn based challenge, and the "Take Turn" event we created a moment ago will be the event that signals a player has taken their turn.


Instead of using a Leaderboard we are going to use a "Scripted Outcome"


Finally we are going to go back to Cloud Code.


Under challenge events you'll see the event you created "Take Turn"


Click that and add something like:

   

//Get our challenge instance Id
var challenge = Spark.getChallenge(Spark.data.challengeInstanceId);

//Create a new variable that will pull in our scores from "ScriptData" which we set in the ChallengeStartedMessage
var playerOneScore = challenge.getScriptData("playerOneScore");
var playerTwoScore = challenge.getScriptData("playerTwoScore");

if(playerOneScore > playerTwoScore)
{
    challenge.winChallenge("Player 1 User Id");
    challenge.setScriptData("whoWon", "Player 1 User Id);
}
else(playerOneScore < playerTwoScore)
{
    challenge.setScriptData("whoWon", "Player 2 User Id);
}
else
{
    challenge.drawChallenge();
} 



You can check the status of a challenge with GetChallengeRequest, which in Unity looks like:


 

new GetChallengeRequest().SetChallengeInstanceId("challengeInstanceId").Send((response) =>
		{
			string userWhoWon = response.Challenge.ScriptData.GetString("whoWon");
		});

 

The code above is purely for reference and you can find out more about the Cloud Code API here: 

https://docs.gamesparks.net/documentation/gamesparks-service-cloud-code-api

Thank you very much for your response ,   It helps me a lot  (especially the "ChallengeStartedMessage" script thing)

I created  two scripts:

A user manager (which is heavily inspired by your youtube tutorial :) )

https://gist.github.com/ovidiubuligan/3d203e435125d8b1e3a5


which is used like:

  

userManager.LoginFacebook ((userDetails)=>{
				// do stuff with user details
			});

  


and a MultiplayerChallenge 'object'  that simulates  a 'quick match' functionality

https://gist.github.com/ovidiubuligan/774203ca67f9c7a67abd

which is used like:

 

var challenge = new MultiplayerChallenge();
multi.JoinOrCreateChallenge((r)=>{
				Debug.Log ("Joined Game");
			},(e)=>{
				Debug.Log ("ERROR JOINING Game");
			});)


I really like the Test Harness since it was the only way I could test 2 players being in a challenge currently:

But I also found 3 strange aspects in the Test Harness:

First:

   If I create a public challenge with the JoinOrCreateChallenge c# code from one user , and when I try to list all ISSUED challenges with "ListChallengeRequest"  i didn't get the open challenge  (it was issued within the 100 min timeframe) . It gives me:

{
 "@class": ".ListChallengeResponse",
 "requestId": "1412113991929",
 "scriptData": null
}

Second:

{
 "_id": {
  "$oid": "542b18b4e4b0bc7fbaf83c94"
 },
 "gameId": 240974,
 "challengeShortCode": "std_multiplayer_challenge",
 "challengeName": "Standard Multiplayer",
 "challenger": "542b1893e4b0bc7fbaf83c4d",
 "challenged": [
  "54280e8be4b09d94b28d2c43"
 ],
 "accepted": [
  "542b1893e4b0bc7fbaf83c4d",
  "54280e8be4b09d94b28d2c43"
 ],
 "declined": [],
 "allAccepted": true,
 "eventType": 0,
 "turnBased": false,
 "turns": {
  "542b1893e4b0bc7fbaf83c4d": []
 },
 "endDate": 1412116500000,
 "state": "ISSUED",
 "accessType": "PUBLIC",
 "minPlayers": 2,
 "maxPlayers": 2,
 "scriptData": {},
 "privateData": {},
 "silent": false
}

  How can  state be "ISSUED" instead of "RUNNING"  when both of the players accepted the public challenge?

Third:

  When Issuing a CreateChallengeRequest , why isn't the challengeId included in the request ?


 I really appreciate any critics since I am new to gamesparks and multiplayer games.


OFF TOPIC:

This is  a preview response because I am still working on grasping the concepts and having some problems with multiple facebook logins on the same machine.

I didn't find how can one login with a  fb user and a test  fb user for an app to quickly test the multiplayer mechanics on the actual game code  with two running instances of facebook app. WebPlayer would have been the fastest way but when I try to login the facebook login dialog doesn't show .(I added it as an website app with localhost as domain  in facebook controll pannel settings)

Ovidiu Buligan, did you manage to solve your problem? I'm trying to do the exact same thing and encounter the same problems as you - challenge stays in "ISSUED" state and cannot be listed, even when it's public.

Hi Lukasz,


Make your challenge so that it is not set to turn based. You need to have two events, one for assigning information to the challenge before the challenge is running, call it "setPlayer1Turn", and one to use as a challenge event, "takeTurn". In your challenge set "takeTurn" as the turn consumer.


setPlayer1Turn takes 2 attributes: 

  • challengeId - (the challengeId of the challenge you wish to update)
  • round1score - (the score you wish to set for round 1)


takeTurn takes 1 attribute:

  • round1score - (the score you wish to set for round 1)


In "Cloud Code" -> "Events" in  for setPlayer1Turn make it look like:

   

//We get the challengeId passed from Unity
var challengeid = Spark.getData().challengeId;

//Get the particular challenge we wish to update with our turns
var challenge = Spark.getChallenge(challengeid);

//We get the dice number for each round
var round1score = Spark.getData().round1score;


//Store the turns in our challenge
challenge.setScriptData("player1round1", round1score);

   

The challenge hasn't changed from "ISSUED" but now it will contain our turns


Back in your game, on the successful response to your challenge being created (it will still be in the "ISSUED" state) you need to call the event to update your challenge. So you need to create a new logEventRequest, set your event key to "setPlayer1Turn" set your attributes ("challengeId", response.challengeId) and your turn ("round1score", score).


When you send this you can check in the noSQL database that your challenge should now contain the turn of the player who created the challenge without the challenge changing from "ISSUED"


When player 2 accepts the challenge they'll receive the scriptData with our score, to calculate the winner you'll need to modify "Cloud Code -> Challenge Event -> Take Turn" to see who won. Then we can call a new logChallengeEvent and post our scores and find out who won!


Shane

Sorry, but this hardly solves anything. I still have a challenge (in the "ISSUED") state, which I cannot access with any player other than the challenger. I can hack it with a logEventRequest of course, but this is not how I want to implement things in my game, come on :)


Here's the detailed description of the my problem: https://support.gamesparks.net/support/tickets/244

Login to post a comment