File size: 5,746 Bytes
d8d884a
 
 
 
 
4dd11c7
d8d884a
 
 
 
3645afb
4dd11c7
d8d884a
4dd11c7
d8d884a
 
4dd11c7
d8d884a
 
6eb30cb
 
 
d8d884a
4dd11c7
 
 
d8d884a
 
 
 
 
 
 
 
 
 
4dd11c7
d8d884a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3645afb
 
 
d8d884a
 
 
 
 
 
3645afb
d8d884a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6eb30cb
4dd11c7
3645afb
 
 
 
d8d884a
90bfb6e
 
 
 
 
 
 
4dd11c7
ea8167e
d8d884a
 
 
 
ea8167e
d8d884a
90bfb6e
 
 
961b061
90bfb6e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import coding
import scratchattach as scratch3
import threading
import time
import random
import os
global conn
global projectid
global endpoint
global users

projectid = os.getenv("ProjectID")
users = {}
session = scratch3.login(os.getenv("Username"), os.getenv("Password"))
class User:
    def __init__(self, name, id):
        self.last_request = time.time()
        self.name = name
        self.id = id
class UserStartup:
    def __init__(self):
        self.last_request = time.time()
class Responder:
    def __init__(self, projectid, channel, can_respond, can_stream, value, user):
        user.last_request = time.time()
        self.user = user
        self.projectid = projectid
        self.can_respond = can_respond
        self.can_stream = can_stream
        self.channel = channel
        self.cooldown = time.time()
        self.wrote = False
        self.last_value = value
    def poll(self):
        value = scratch3.get_var(self.projectid, "channel "+str(self.channel))
        if value != self.last_value and value != None:
            self.user.last_request = time.time()
            self.last_value = value
            binary = coding.decimal_to_binary(int(value))
            if binary[1:3]=="01":
                value = str(coding.binary_to_decimal(binary[3:]))
            if binary[1:3]=="10":
                value = str("true" if binary[3:] == 1 else "false")
            if binary[1:3]=="11":
                value = coding.convert_to_text(binary[3:])
            
            return value
    def close(self):
        if self.can_stream or not self.can_respond:
            conn.set_var("channel "+str(self.channel), "0")
        else:
            while str(scratch3.get_var(self.projectid, "channel "+str(self.channel))) !="0":
                pass
    def respond(self, response):
        global conn
        if self.wrote and not self.can_stream:
            raise Exception("Can't stream to this as a response")
        if not (self.can_respond or self.can_stream):
            raise Exception("Can't respond to this")
        while time.time() - self.cooldown < 0.5:
            time.sleep(0.5 - (time.time() - self.cooldown))
        if self.can_respond or self.can_stream:
            payload = "1"
            if type(response) is int:
                payload+="01"+coding.decimal_to_binary(response)
            elif type(response) is bool:
                payload+="10"+"1" if response else "0"
            elif type(response) is str:
                payload+="11"+coding.convert_to_binary(response)
            self.last_value = str(coding.binary_to_decimal(payload))
            conn.set_var("channel "+str(self.channel), str(coding.binary_to_decimal(payload)))
            t= time.time()
            times=0.2
            while scratch3.get_var(self.projectid, "channel "+str(self.channel)) !=str(coding.binary_to_decimal(payload)):
                if time.time()-t>=times:
                    print("Message not sent, retrying")
                    times+=0.1
                    conn.set_var("channel "+str(self.channel), str(coding.binary_to_decimal(payload)))
                    t=time.time()
        self.wrote = True
class ConnectionEndpoint:
    def receivedMessage(self, message, user, responder):
        global users
        r=random.randrange(1, 2047)
        while r in users:
            r=random.randrange(1, 2047)
        users[r] = User(message, r)
        responder.respond(r)
        responder.close()
class HeartbeatEndpoint:
    def receivedMessage(self, message, user, responder):
        responder.close()

def thread(n):
    global users
    global conn
    global projectid
    global endpoint
    heartbeater = HeartbeatEndpoint()
    conn.set_var("channel "+str(n), "0")
    while True:
        
        value = scratch3.get_var(projectid, "channel "+str(n))
        if str(value) != "0" and value != None:
            binary = coding.decimal_to_binary(int(value))
            reqendpoint = coding.binary_to_decimal(binary[1:6])
            header = coding.binary_to_decimal(binary[6:17])
            staticpayload = binary[17] == '1'
            streamingpayload = binary[18] == '1'
            acceptstaticpayload = binary[19] == '1'
            acceptstreamingpayload = binary[20] == '1'
            payload = None
            if staticpayload and not streamingpayload:
                payloadformat = binary[21:23]
                if payloadformat == "01":
                    payload = str(coding.binary_to_decimal(binary[23:]))
                if payloadformat == "10":
                    payload = "true" if binary[23:]=='1' else "false"
                if payloadformat == "11":
                    payload = coding.convert_to_text(binary[23:])
            if header in users:
                user = users[header]
            else:
                user = UserStartup()
            respond = Responder(projectid, n, acceptstaticpayload, acceptstreamingpayload, value, user)
            if reqendpoint == 31:
                heartbeater.receivedMessage(payload, user, respond)
            else:
                endpoint[reqendpoint].receivedMessage(payload, user, respond)

def monitor_users():
    global users
    while True:
        time.sleep(1)
        for k, v in users:
            if time.time() - v.last_request >= 300:
                del users[k]
def start_server(endpoints):
    global projectid
    global conn
    global endpoint
    endpoints.insert(0, ConnectionEndpoint())
    endpoint = endpoints
    conn = session.connect_cloud(projectid)
    threads = [threading.Thread(target=thread, args=(i+1,)) for i in range(10)]
    for t in threads:
        t.start()
    monitorusers = threading.Thread(target=monitor_users)
    monitorusers.start()