question

Matt avatar image
Matt asked

The Core of using Custom Game Server

Hiya folks,

I'm developing a mobile game with multiplayer features. By design, I need the following:

- Player chooses to enter a PvP battle

- They get matched up against someone of roughly equal rank/skill

- They each play their own game but randomly will send an event to the other player

- The events can be chat emojis or gameplay-related hindrances and buffs.

- I may need to sync up a value between them so they can see their opponent's progress.

- When one player loses, the game ends and rank is adjusted.

I'm looking into using a custom game server for this, but I have absolutely no experience and I'm encountering so many questions. I downloaded this example from GitHub and followed the steps. My problem, now, is I don't know how this is expected to be used in an actual game.

For instance...

1. When a player selects 'Battle' to play a PvP match, do I call PlayFab's MatchMake?

2. Is the build I make the single instance that handles everything or is it per battle?

3. Inside my Mediator, do I need to have a List<> of every player currently being matched?

4. Do I call StartGame when 2 players are matched up?

5. Where do I store the 2 battling players so I can send events back and forth?

6. When I call MatchMake, does that run until a match is found or some time expires?

There are so many other questions I have, but they all stem from the core concept I think, and once I understand that I'll be good to go. I haven't been able to find any, but if there's an example somewhere that's actually using all of the Matchmaking/Multiplayer functionality, that'd be a huge help. The example on GitHub has been very helpful in setting up the build.

Thanks again!

-Matt

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image
brendan answered

Some of the answers depend upon your requirements and design, but at a high level:

1. The PlayFab matchmaker simply finds the first available slot open in a session matching the requirements. If you use a statistic as part of that, it'll find the first available slot in a server where the statistic is the closes match possible. It's not a queued system where it waits some set period of time to see if someone with a closer statistic shows up - it just finds an open slot or, if none are available that match the required parts of the search (game mode, region, build version, any tag filters), it can start a new instance (if you tell it to). If you'd prefer a queued match experience, you could write your own matchmaker (using our Matchmaker API) or run a server which is the "waiting room" that players join to find someone of equal skill (possibly hosting chat there, as well).

2. In our hosting, we run the executable and match players into it based on the settings you give us (for the game mode). For a 1-vs-1 game, we highly recommend writing your game server executable to manage many 1-vs-1 sessions in a single instance of the executable, for efficiency.

3. If you're using our matchmaker, you shouldn't need to track on all the players joining games.

4. You can either start new instances from the Matchmake call or separately, depending on how you want to manage the sessions. Each successful Matchmake call returns IP and Port info for the server, so that the player can join.

5. Not sure what you mean. The long-term storage of the player and all their stats/inventory/data/etc. is in PlayFab, in various data systems. When a player joins a server, you would read that from PlayFab, and then manage the state of the player throughout the session on that server, updating the player account at the end (possibly with some interim updates while the session is running - particularly if sessions can go long).

6. No. It immediately returns an available slot, or an error that none are available.

I'd also highly recommend reviewing this tutorial: https://api.playfab.com/docs/tutorials/landing-tournaments/custom-game-servers

15 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Matt avatar image Matt commented ·

Thanks for the information, that is very helpful and things are slowly becoming clearer to me, though maybe that's just an illusion. I say that because my MatchMake call is returning a 500 Internal Server error when I call it from the client. I added the 'test' Game Mode (my titleID is AC50). I didn't touch any of the command line arguments (not sure if I was supposed to).

My call is:

PlayFab.PlayFabClientAPI.Matchmake( new PlayFab.ClientModels.MatchmakeRequest()
{
	BuildVersion = "arena_v0",
	Region = PlayFab.ClientModels.Region.USEast,
	GameMode = "test"
},
( result ) =>
{
	Debug.Log( "Matchmake returned: " + result.Status );
},
( error ) =>
{
	Debug.Log( "Matchmake error: " + error.ErrorMessage );
} );

and my server just has this inside the OnRegister of its Mediator:

NetworkServer.RegisterHandler( MsgType.ObjectDestroy, Test );
NetworkServer.RegisterHandler( MsgType.Error, Test );
NetworkServer.RegisterHandler( MsgType.Ready, Test );
NetworkServer.RegisterHandler( MsgType.NotReady, Test );
NetworkServer.RegisterHandler( MsgType.AddPlayer, Test );
...



void Test( NetworkMessage msg )
{
	Debug.Log( "Network Message: " + msg.msgType);
}

Thanks for the help!

0 Likes 0 ·
Matt avatar image Matt Matt commented ·

After a few failed attempts with my own code, I decided to use what was in the PlayFab example on GitHub; however, I still am not getting any calls when a client connects to the server.

Inside the CustomNetworkManager you guys wrote, I uncommented the OnServerConnect function, for example, and added a debug log. There are also already debug logs in the mediator you guys wrote for when connections are made.

None of them seem to get hit, but I can confirm the network manager is being instantiated since the Logger dispatch in the SetupUnityNetworkingCommands gets hit.

There is basically none of my own code here and I still don't get any calls when someone connects (or at least not debug logs telling me so), so I'm hoping you guys might be able to pinpoint the problem here for me!

Thanks!!

1 Like 1 ·
brendan avatar image brendan Matt commented ·

What I'm seeing in the logs is a failure to start the game session after multiple attempts. Can you confirm that you are first setting the game build to be active in a region, and waiting for your first server to reach the "ready" state?

0 Likes 0 ·
Matt avatar image Matt brendan commented ·

I believe so, yes. I set the Build to work in USEast and then the server popped up under the server tab. It said it was running, and I tested after about 25 minutes and I still got the 500: Internal Server Error.

Honestly there's probably a very simple step I missed that most people would know to do, so I wouldn't assume the obvious things are setup correctly. I *did* follow the tutorials though pretty well I think.

0 Likes 0 ·
Show more comments
Matt avatar image Matt Matt commented ·

Do I need to interact with PlayStream to know when clients connect? I tried registering a handle using the Unity MsgType Connect, for instance, but I got an error in the log saying I can't change it.

Besides knowing when players connect, how do I know on the server which 2 players have been paired up for their battle?

0 Likes 0 ·
brendan avatar image brendan Matt commented ·

When the client connects to the server, the network layer you're using should have something to indicate that. In Unity, it's the NetworkingContext Mediator, for example. Have a look at our example server code to see a sample of this.

As to pairing players, that's specifically what the server should be doing. The Matchmake call isn't pairing players - it's finding a single open slot for the local player in a game server, and telling the client to join that server. As players join, the server should be pairing them up and starting their sessions.

0 Likes 0 ·
Show more comments
Show more comments
marcowilliamspf avatar image
marcowilliamspf answered

@matthew .. I'm a little late to the conversation. How are you testing this out?

This sounds to me like you are trying to connect to our custom game server, and listen for when connections are made.

There is a two step process that happens and the client code needs to reinforce this.

Step 1> Client Uses MatchMaker API or (if local) will connect locally via localhost and port 7777. You have to initiate the connection via the client once either match maker returns or directly to your local server.

step 2> Client needs to be listening for NetworkMessageType.Connect

Client.RegisterHandler(NetworkMessageType.Connect, OnNetworkConnect);
Client.RegisterHandler(NetworkMessageType.Error, OnClientNetworkingError);
Client.RegisterHandler(NetworkMessageType.Disconnect, OnClientDisconnected);
step 3> client needs to send a message back to the server to Authenticate. You can see an example of this in the sample client.
    [Serializable]
    public class AuthTicketMessage : MessageBase
    {
        public string PlayFabId;
        public string AuthTicket;
        public bool IsLocal;
    }


You must pass your AuthTicket ( received from Matchmaker ) or if using a local server, pass your SessionTicket from logging into PlayFab.

if you do not do this step, the server will reject your connection and disconnect you.

Out of the box, our sample client works with our sample server. You can run the server locally, and make sure that "IsLocal" is checked on the script in the Test Scene (StartupScene).


This is the example script we use to connect to the server: Here

Note: That on the server, you should not have to do ANYTHING to make connection work. We have handled all of this for you. As long as you are implementing the client correctly, it should work.

Now to address your real concern of what you are trying to do. Sounds like you want to make a match making server component and I will openly admit I am a little tempted to make one as a working example and include that as part of our default custom game server ( LOL ).


Here is what you want to do for that.

1> Create a custom message that extends from MessageBase called NetworkMatchRequest, and a matching class called NetworkMatchResponse.

Your Model should look something like this.

public class MatchMakerMessageTypes {
    public static short NetworkMatchRequest = 600; // use whatever number you want.    
    public static short NetworkMatchResponse = 601;
}

public class NetworkMatchRequest(){    
    public string PlayFabId;
    public int Rank;
}

public class NetworkMatchResponse(){   
    public string RoomId;
    public string RoomMembers;

}


So now you have messages, you need to have these models on both server & client.

Client will send a NetworkMatchRequest message via UNET, and the server will respond with the RoomID and a list of members in that room.

Granted, this is all very arbitrary & pseudo as I'm presenting you with a concept, you'll need to also create structures for your rooms and create logic to join them into the room when they request etc..

2> The second part, is to do some sort of logic, so you need to listen on the server for the NetwokrMatchRequest message. To do this, Create a view & mediator and register it (See MySample) for creating a strange package or look at one of the other examples.

In the override for OnRegister do something like this.

//NOTE: Don't forget to inject your unity networking object.

[Inject] public UnityNetworkingData UnityNetworkingData { get; set; }

public override void OnRegister(){    

     NetworkServer.RegisterHandler(MatchMakerMessageTYpes.NetworkMatchRequest, OnMatchRequest);

}


private void OnMatchRequest(NetworkMessage netMsg){
    //Note you need to read the message.     
    var message = netMsg.ReadMessage<NetworkMatchRequest>();    
    //DO SOME Match Making logic here....
    //Maybe join current player into the room that the other player is already in. Or however you want it to work.    

    //Send message back to client.    

    var conn = UnityNetworkingData.Connections.Find((c)=>{ return c.PlayFabId == message.PlayFabId; });

    if(conn != null){

	conn.connection.Send(MatchMakerMessageTypes.NetworkMatchResponse, new NetworkMatchResponse(){    

     RoomId = "" // <-- put your room data here.    
     Members = "" // <-- pass a list of PlayFab Id's here.. });    

}

}

I just whipped this up, don't even know if there are typo's or etc.. it is meant for a general concept of doing what you want. There is obviously more work to do, but this should point you in the right direction.



Conclusion, start with the Out of the Box, make sure you have a client script that can connection & Authenticate. everything you need for the client is here: https://github.com/PlayFab/PlayFabGameServer/tree/master/PlayFabGameServerClientExample


Once you've done that, create your mediator in the server, make sure your bindings are setup in your PackageManager ( very important ). Throw your view on a game object in the Game Server scene, and you are read to listen for custom messages.

hope that helps.

9 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Matt avatar image Matt commented ·

Thanks Marco!

There's a lot here, but things are much clearer. I'll dive into this, well, probably not until next week. This should be plenty to get me on the right path though!

Thanks again,

-Matt

0 Likes 0 ·
Matt avatar image Matt Matt commented ·

Inside the UnityNetworkManagerMediator, there's a co-routine looking to shutdown the server if there are no connections. Will the server shutdown if it's the last instance even though the minimum is set to 1? If so, how does that affect costs if I have a server up during testing and every 2 minutes (or whatever time is set) it goes down then up?

0 Likes 0 ·
brendan avatar image brendan Matt commented ·

That's the server instance code, which is separate from the code which controls when server hosts are started or stopped. If you have a build active in a region, you'll always have at least one server host running there. The only time a server host is shut down for a title is if there are other server hosts running in the region, and they must have enough free slots for new instances of game servers to run (the min free slots setting).

0 Likes 0 ·
Show more comments
Matt avatar image
Matt answered

Following the example, everything is working great. The setup is clean and simple also, which I love. I'm realizing now, though, that there is something I'd like to continously sync between the players. Would it be a bad idea to just use the Connection.Send every frame from the client to server and then from server to the other client?

I've used RPCs where it was kind of handled for you, I'm not sure in the case where we're using a NetworkClient directly (or even using the NetworkManager which seemed completely unnecessary for me).

3 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan commented ·

Most games - even shooters - generally don't update every frame, as it's not necessary. They usually send an update about 6 to 10 times a second, depending on the needs of the game. Those with less fast action requirements are even less frequent.

0 Likes 0 ·
Matt avatar image Matt brendan commented ·

I see. I ask because I recall using some libraries (Photon I think was one) where you could mark a function as an RPC and it'll just automatically sync constantly. I don't see that in Unity, but for my game, I definitely only need to refresh every couple seconds, so I won't worry about an optimized means of sending the data (if there is one) and just use the NetworkClient.Send call.

Thanks!

0 Likes 0 ·
joehot200 avatar image joehot200 Matt commented ·

I know this is an old thread, but you know you can change the rate of sending with unity networking, I presume? You just use [NetworkSettings(channel = 0, sendInterval = 0.2f)]

And change the interval as you wish.

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.