Reputation: 976
I've asked this question on official Photon Server forums, but it's less active than this website, so there might be people who understand what I'm talking about, so please, if you have time and knowledge, share it. Thank you!
Here to comes...
So, I've got a really good working prototype of a server on Photon and a basic Unity3D client that talks to the Server. It was built from the examples on cjrgaming.
The client can: Connect, send request, establish and send encrypted request The server can: Create peer, receive operation request, send operation response or event to the client, as well, my small addition was: If a game has a lot of operations, you don't have to use a huge switch case statement, but rather, I have divided operations into categories (Classes) and I invoke them by using delegates and dictionary.
I will post a working examples of it when I feel like it's ready to be posted, but now, to my actual question.. (Sorry for the long post, I had to explain what I know and what I have so far):
What is actually an operation sent from client to the server? Or an Event raised by the server to client (all clients at once?)?
At first, I was thinking that each operation is particular user flow in the game. For example, operation code "1", means a Player X wants to shoot Player Y, do something. But then, I realized, that you cannot put all your game logic in only 255 operations, as per byte limit, without extending it to short int or something.
Then I found out that there's a channelID as well, which can be different on the same operation code request... which means for me, an operation code is not a user flow, but a data stream of the same/similar actions between client and server, and the channelID can be used to differentiate between requested operations to be calculated on the server.
Then...! I realized (oh dummy me), that there's parameters sent from client to server and vise versa in a dictionary, which adds another layer of possible user flows.
So.. now I suppose to understand things, but they just confused me even more.
Can anyone briefly explain the purpose of operation/event/channelID? For example, if you do a small multiplayer game, what you will use to make user (game) flows, like -> A Player hitting a target, player picks up an item in the world, player sends message. Would you use unique operation code for each of this flows, or you group your operations by meaning and use channels to differentiate between requests, or even here, you use same channelID for many user flows and differ them with some ID inside parameters?
Hope I made any sense.
Thanks a lot guys, for time, at least, for help if any!
Upvotes: 3
Views: 6339
Reputation: 2472
1) Channels are a totally different topic and have nothing to do with differentiating between different kind of game logic, that you want to trigger. Instead channels are there for determining priorities and to state, if an operation depends on another one.
a) If you send a shoot-operation and a 2 chat-operations and as the network-connection of the client isn't the best, the first of the chat-messages goes lost on the way, but as you have stated to send it reliably the Photon client will automatically resend it, when the server isn't acknowledging, that it has received it. Now the other chat-message should be hold back by Photon, until the first one could successfully be sent, as otherwise the order of the displayed chat-messages from the same author would not be the one anymore in which they have been written. Now the problem is, that not only operations of the same type should be hold back, but maybe there are other operations, which should only be sent, after the lost one has been repeated (for example the visible results of a "user leaves the chat" operation should not be displayed on the screen, until after his last chat-message, that he has sent before leaving). On the other hand not all operations of the same type have to be held back. For example the player could talk to 2 different other users in private and when one message to one of them doesn't make it through instantly, then there is no reason to hold all messages to the other one back. Photons solution to this problem is channels: Send all operations that depend on each other, in the same channel, but send operations, that are independent from them, in another one. If now one of the operations has to be repeated, operation in other channels won't be hold back, but the ones in the same channel will.
b) Another means of channels is, to determine priorities: the lower the channel ID, the higher the priority. So if you have some small amount of high priority data and other data, that has only low priority, but can happen to appear in huge amounts, then it would be a good idea, to send the high priority data on a channel with a lower channel-ID. This way it will still immediately get out, although in channels with higher IDs maybe a lot of data has already been queued for sending, that has not been sent out yet.
2) Operations and events are both messages. Actually there are three types of messages: operationRequest, operationResponse and event.
a) A client can send an operationRequest with a certain operation code to the server via PhotonPeer.opCustom(). Some typical operations like joining and leaving rooms are already implemented by application from the Photon application layer like the Lite or the LoadBalancing application. If there is a client side API provided for a Photon application, then this API can provide functions like opJoin() out of the box, that wrap the call with the correct parameters to opCustom(), like the client side APIs of the mentioned applications do.
b) Depending on how an operation with a certain code is implemented on the application level, the server might send an operationResponse to the client, from which it has received the operationReqeust. For example LitePeer.opJoin() will trigger a join response from the server, but LitePeer.opRaiseEvent won't trigger a response.
c) Depending on how the operation is implemented on the application level, the server may or may not send events out to certain clients. For example LitePeer.opJoin() will trigger a join event, that will be sent to all players in that room. LitePeer.opRaiseEvent() will on default let the server raise an event, that is holding the payload, that you have passed to it, for all clients in the same room except the sending one. However opRaiseEvent() offers its calling client the possibility to specify the receiving clients.
3) "A Player hitting a target, player picks up an item in the world, player sends message" If you need any special server side logic for these, then you would implement them as their own operations with different code. If that is not the case, then you would send these informations with opRaiseEvent(), but you would still specify a different eventCode for each of those 3. However operation- and eventCodes are intended to be used to differ between different kinds of informations, like a chat-message, a pickup a hit, a position update, etc. So player a tells player b that he has hit player c and player b tells player d, that he has hit player d, would both use the same code. The information about Which player has been hit would - same like how hard it has been hit or with which weapon - belong into the payload of the operation. So you would for example pass the player-ID of the player, that has been hit, the munition-type and how much life-energy he has lost, as payload to opRaiseEvent() or opCustom().
Upvotes: 14