tw_serverinfo.network.Network.send_packet()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 14
nop 5
dl 0
loc 27
ccs 13
cts 13
cp 1
crap 4
rs 9.7
c 0
b 0
f 0
1 1
import logging
2 1
import secrets
3 1
import socket
4
5 1
from tw_serverinfo.models import Server
6
7
8 1
class Network(object):
9
    # www.teeworlds.com has no AAAA domain record and doesn't support IPv6 only requests
10 1
    PROTOCOL_FAMILY = socket.AF_INET
11
12
    # connection timeout in ms
13 1
    CONNECTION_TIMEOUT = 1000
14
15
    # sleep duration between checking if we received a packet in ms
16 1
    CONNECTION_SLEEP_DURATION = 50
17
18 1
    PACKETS = {
19
        'SERVERBROWSE_GETCOUNT': b'\xff\xff\xff\xffcou2',
20
        'SERVERBROWSE_COUNT': b'\xff\xff\xff\xffsiz2',
21
        'SERVERBROWSE_GETLIST': b'\xff\xff\xff\xffreq2',
22
        'SERVERBROWSE_LIST': b'\xff\xff\xff\xfflis2',
23
        'SERVERBROWSE_GETINFO_64_LEGACY': b'\xff\xff\xff\xfffstd',
24
        'SERVERBROWSE_INFO_64_LEGACY': b'\xff\xff\xff\xffdtsf',
25
        'SERVERBROWSE_GETINFO': b'\xff\xff\xff\xffgie3',
26
        'SERVERBROWSE_INFO': b'\xff\xff\xff\xffinf3',
27
        'SERVERBROWSE_INFO_EXTENDED': b'\xff\xff\xff\xffiext',
28
        'SERVERBROWSE_INFO_EXTENDED_MORE': b'\xff\xff\xff\xffiex+',
29
    }
30
31 1
    @staticmethod
32 1
    def send_packet(sock: socket.socket, data: bytes, extra_data: bytes, server: Server, add_token=True) -> None:
33
        """Generate or reuse a request token  and send the passed data with the request token to the passed socket
34
        Returns the updated server dict with additional token on game server types
35
36
        :type sock: socket.socket
37
        :type data: bytes
38
        :type extra_data: bytes
39
        :type server: dict
40
        :type add_token: bool
41
        :return:
42
        """
43 1
        if not server.request_token:
44 1
            server.request_token = secrets.token_bytes(nbytes=2)
45 1
            logging.log(logging.DEBUG, 'generated server request token: {token!r}'.format(token=server.request_token))
46
47 1
        packet = b'%s%s\0\0%s' % (extra_data, server.request_token, data)
48
49 1
        if add_token:
50 1
            if not server.token:
51 1
                server.token = secrets.token_bytes(nbytes=1)
52 1
                logging.log(logging.DEBUG, 'generated server token: {token!r}'.format(token=server.token))
53 1
            packet += server.token
54
55 1
        logging.log(logging.DEBUG, 'sending packet ({packet!r}) to {ip:s}:{port:d}'.format(packet=packet, ip=server.ip,
56
                                                                                           port=server.port))
57 1
        sock.sendto(packet, (server.ip, server.port))
58
59 1
    @staticmethod
60 1
    def receive_packet(sock: socket.socket, servers: list, callback: callable) -> bool:
61
        """Check if we received a packet if yes check for the servers with the ip and port
62
        and pass the server together with the data to the processing function given as a callback
63
64
        :param sock:
65
        :param servers:
66
        :param callback:
67
        :return:
68
        """
69 1
        try:
70 1
            data, addr = sock.recvfrom(2048, 0)
71 1
        except BlockingIOError:
72 1
            return False
73
74 1
        logging.log(logging.DEBUG, 'received data ({data!r}) from {ip:s}:{port:d}'.format(data=data, ip=addr[0],
75
                                                                                          port=addr[1]))
76 1
        for server in servers:  # type: Server
77 1
            if server.ip == addr[0] and server.port == addr[1]:
78 1
                callback(data, server)
79 1
                return True
80
        return False
81