📜 ⬆️ ⬇️

Python library for Photon Server

Over the past few months, I have had to become closely acquainted with the framework for developing client-server games Photon . In this article, I will not dwell on the pros and cons of Photon, as for this, perhaps, you need experience with more than one framework. It will be a question of library for work with Photon Server in the Python language.

First, a few words about why it was needed at all and why I did not use one of the already existing client libraries.

In the process of working on the server part of the game project, we often had service tasks, such as getting current data directly from the application, graceful stopping the game service (you can’t just cut down the game server while people are on it). At the very beginning, a small GUI application was written to work with all the service parts of the application, through which these problems were solved. But over time, we increasingly wanted to work with these office things through the console, and even Unix. At the same time, I did not want to recompile everything with the slightest code changes. For this reason, C #, Java, and C ++ have been flagged. There was still a variant with JavaScript and node.js, but Python attracted me for a long time and I really wanted to get to know it. And then such a good case. Therefore, it was decided to write the library in this language.

Since the Photon protocol is not publicly available (or, in any case, I could not find it), I had to decompile the source code of the client library written in Java, study them and rewrite the whole thing in Python. Java was chosen because this language is closer to me, since the last few years I have been developing in this language. As it turned out later, after a conversation with the Photon developers, it was better to take the C # library, since it is more recent.
')
Initially, I wanted to write everything in Python version 2.7 (at the time of writing the current version of the library was 2.7.9), but faced with the difficulty in implementing some things that were already implemented out of the box in 3.4, I decided to keep up with the times .

The source of the library can be found on github .

Let us analyze a small piece of code that uses this library. The interfaces almost completely coincide with libraries in other languages, so for those who are familiar with Photon, there will be nothing new here.

First, let's implement the PeerListener interface:

class Connection: def __init__(self, connected=False): self.connected = connected class SimpleListener(PeerListener): def __init__(self, connection): super().__init__() self.connection = connection def debug_return(self, debug_level, message): print("[{}] - {}".format(debug_level.name, message)) def on_status_changed(self, status_code): print("[Status changed] - {}".format(status_code.name)) if status_code is StatusCode.Connect: self.connection.connected = True def on_operation_response(self, op_response): print(op_response) def on_event(self, event_data): print(op_response) 

We will not process the OperationResponse and the Event yet. Let's just output them to the console. It also uses a small Connection class, just like holder for the connection status. It is useful to us later.

Now we’ll write a service thread that will pull the service method from our peer about 10 times per second (developers recommendation):

 class ServiceThread(threading.Thread): def __init__(self, pp): threading.Thread.__init__(self) self.pp = pp self._run = False def run(self): self._run = True while self._run: self.pp.service() time.sleep(100.0 / 1000.0) def stop(self): self._run = False 

And of course the main function:

 def main(): connection = Connection() pp = PhotonPeer(enums.ConnectionProtocol.Tcp, SimpleListener(connection)) pp.set_debug_level(DebugLevel.All) pp.connect(your_ip, your_port, your_app_name) service_thread = ServiceThread(pp) service_thread.start() while connection.connected is False: pass # Put your code here service_thread.stop() service_thread.join() pp.disconnect() 

Entirely source here
 import threading import time from photon import enums from photon.enums import DebugLevel, StatusCode from photon.listener import PeerListener from photon.peer import PhotonPeer class Connection: def __init__(self, connected=False): self.connected = connected def main(): connection = Connection() pp = PhotonPeer(enums.ConnectionProtocol.Tcp, SimpleListener(connection)) pp.set_debug_level(DebugLevel.All) pp.connect(your_ip, your_port, your_app_name) service_thread = ServiceThread(pp) service_thread.start() while connection.connected is False: pass # Put your code here service_thread.stop() service_thread.join() pp.disconnect() class ServiceThread(threading.Thread): def __init__(self, pp): threading.Thread.__init__(self) self.pp = pp self._run = False def run(self): self._run = True while self._run: self.pp.service() time.sleep(100.0 / 1000.0) def stop(self): self._run = False class SimpleListener(PeerListener): def __init__(self, connection): super().__init__() self.connection = connection def debug_return(self, debug_level, message): print("[{}] - {}".format(debug_level.name, message)) def on_status_changed(self, status_code): print("[Status changed] - {}".format(status_code.name)) if status_code is StatusCode.Connect: self.connection.connected = True def on_operation_response(self, op_response): print(op_response) def on_event(self, event_data): print(op_response) if __name__ == "__main__": main() 


Separately, I would like to dwell on the serialization of integer types. For those who managed to look into the sources, such code may seem a bit strange:

 def _serialize_byte(out, value, set_type): if set_type: out.extend(bytearray([98])) value = ((value + ((1 << 7) - 1)) % (1 << 8)) - ((1 << 7) - 1) out.extend(struct.pack('>b', value)) 

This is an example of byte serialization; for other types, everything is the same. The answer is simple - similar code is here for compatibility with the result of serialization in Java.

And finally, about the current limitations:

It would be great to add the UDP protocol and handle the last 2 points, but there is no time for it yet. But the source is open, as they say - all the cards in hand. Hopefully, we are not the only ones who need it and our work will be useful to someone.

PS If someone finds errors / inaccuracies in the code and / or not optimally written sections (which I do not rule out due to not very long acquaintance with Python) - I will be immensely grateful for pointing them out.

Source: https://habr.com/ru/post/254455/


All Articles