This is a read-only archive of the old Scratch 1.x Forums.
Try searching the current Scratch discussion forums.

#1 2011-12-17 12:03:30

Magnie
Scratcher
Registered: 2007-12-12
Posts: 1000+

Python Network Mirror and Scratch Live Clone

This topic will hold and link to info about how to set up your own Scratch Live with Python.

First, you will want Python. You can get that here. After you download and install it, you will want to create the server and client. Before that, you may want a general overview on how to connect Python to Scratch which is here.

This is the Python Server ( with Scratch Live as it's job ) if you want to host the Scratch Live on your computer, you will need to port-forward:

Code:

# Scratch Server
# Version 1.0.0
# By: Magnie

import threading
import socket
import sys
from array import array

class Server: 
    def __init__(self): 

        # Server Information
        self.host = '' # Game host
        self.port = 42002 # Game port
        self.backlog = 5 # Maximum clients?
        self.size = 1024 # Maximum receive size
        self.server = None 
        self.threads = [] # Client threads ( just a record )
        self.variables = {} # user : value

        # Game Information

    def open_socket(self): 
        try: # Try settings up the connection
            self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Define socket type
            self.server.bind((self.host,self.port)) # Bind server to port
            self.server.listen( self.backlog ) # Wait for clients to connect
        except socket.error, (value,message): # If it can't connect Then print out the error and shut down the server.
            if self.server: 
                self.server.close() 
            print "Could not open socket: " + message 
            sys.exit(1)

    def run(self):
        self.open_socket()
        running = 1
        id = 0 # To define each client.
        while running:
            id += 1 # Increase so no client has the same ID.
            c = Client(self.server.accept()) # Waits for a client then accepts it.
            c.start() # Starts it.
            self.threads.append(c) # Adds it to a list so the variable c and be used for the next client.

        # Disconnect all clients.
        self.server.close() # Disconnect socket.
        for c in self.threads: # For each thread
            c.join() # End that thread.

class Client(threading.Thread): 
    def __init__(self,(client,address)): 
        threading.Thread.__init__(self) 
        self.client = client 
        self.address = address 
        self.size = 1024 
        self.follow = []
        
        print self.address, 'has connected.'

    def run(self): 
        running = 1 
        while running:

            data = self.client.recv(self.size) # Wait for data.
            data = parseScratchCommand(data) # Parse the data.
            print data # Print it
            if data[0] == 'sensor-update': # If a variable is updated

                continue

            elif data[0] == 'broadcast': # If a broadcast is sent
                
                data[1] = data[1][1:]
                data[-1] = data[-1][:-1]
                print data
                
                if data[1] == '': continue
                
                if data[1][0] == '<' or data[1][0] == '>':
                    if data[1][1:] not in s.variables:
                        s.variables[ data[1][1:] ] = ""
                        
                if data[1][0] == '>':
                    vari = data[1][1:]
                    value = s.variables[ vari ]
                    self.client.send( sendScratchCommand('sensor-update "'+vari+'" "'+value+'"') )
                    
                    for follow in s.threads:
                        follow.follow_update(vari)
                    
                elif data[1][0] == '<':
                    vari = data[1][1:]
                    value = ' '.join( data[2:] )
                    s.variables[ vari ] = value
                    self.client.send( sendScratchCommand('sensor-update "'+vari+'" "'+value+'"') )
                    
                    for follow in s.threads:
                        follow.follow_update(vari)
                    
                elif data[1][0] == '^':
                    vari = data[1][1:]
                    self.follow.append( vari )
                    
                else:
                    continue
            
            else: 
                self.client.close() # Close the connection.
                print self.address, 'has closed the connection.' # Debugging purposes and just informative if you are watching it.
                running = 0 
                
    def follow_update(self, vari):
        if vari in self.follow:
            value = s.variables[ vari ]
            self.client.send( sendScratchCommand('sensor-update "'+vari+'" "'+value+'"') )
            self.client.send( sendScratchCommand('broadcast "^follow_update"') )
            
def parseScratchCommand(cmd):
    cmd = cmd[4:] # Remove the first four letters ( it's annoying to deal with )
    cmd = cmd.split(' ') # Split the string where there is a space. It helps to find out broadcasts.
    return cmd # Return the list. ['broadcast/sensor-update', 'valueWord1', 'valueWord2']

def sendScratchCommand(cmd): # I was never sure what this did, but it's required.
    n = len(cmd)
    a = array('c')
    a.append(chr((n >> 24) & 0xFF))
    a.append(chr((n >> 16) & 0xFF))
    a.append(chr((n >>  8) & 0xFF))
    a.append(chr(n & 0xFF))
    return (a.tostring() + cmd)

if __name__ == "__main__":
    s = Server() 
    s.run()

This is the Client, if you want other people to connect, they have to run this file as well:

Code:

# Scratch Mirror
# Version 1.5.0
# By: Magnie

import socket # Network base
import time # For delaying purposes mostly.
import threading # So it can send and receive at the same time anytime.

# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
# CHOST is the IP Scratch is running on, if you are running it    #
# on this computer, then the IP is 127.0.0.1                      #
# Theoretically you could run this Mirror on another computer.    #
# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
# CPOST is the port Scratch is listening on, the default is       #
# 42001. Usually this is only change by a Scratcher who knows a   #
# about Squeak and networking with sockets.                       #
# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
CHOST = '127.0.0.1'
CPORT = 42001

# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
# SHOST is the IP the server is running on.                       #
# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
# SPORT is the port that the server is using. 42002 is the        #
# unofficial port for Scratch Servers. The host will need to make #
# sure to port-forward the port so people can connect from the    #
# internet.                                                       #
# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
SHOST = '127.0.0.1'
SPORT = 42002

# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
# Some extra settings that are more for advanced users are below. #
# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #

# Time between checking the threads for broken ones.
THREADCHECK = 5

class Client(threading.Thread): # This class listens for messages sent from Scratch and sends it to the Server.
    def __init__(self, CHOST, CPORT):
        threading.Thread.__init__(self) # Initialize it, basically just separate it from the main thread.
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Defines the type of connection ( UPD, TCP on IPv4 or IPv6 )
        print("Connecting to Scratch...")
        self.sock.connect((CHOST, CPORT)) # Connect to Scratch
        print("Connected to Scratch!")
        
    def run(self):
        global running
        print "Listening for Scratch messages."
        while running:
            data = self.sock.recv(1024) # Wait for something to be sent to the mirror
            self.send(data)
    
    def send(self, message):
        server.sock.send(message) # Send the data to the server.

class Server(threading.Thread): # This class listens for messages from the Server and sends it to Scratch.
    def __init__(self):
        threading.Thread.__init__(self, SHOST, SPORT) # Initialize it, basically just separate it from the main thread.
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Defines the type of connection ( UPD, TCP on IPv4 or IPv6 )
        print("Connecting to Scratch Server...")
        self.sock.connect((SHOST, SPORT)) # Connect to the Server.
        print("Connected to Server!")
        
    def run(self):   
        global running
        print "Listening for Server messages."
        while running:    
            data = self.sock.recv(1024) # Listens for messages sent from the Server.
            self.send(data)
    
    def send(self, message):
        scratch.sock.send(message) # Sends messages to Scratch.

running = 1
scratch = Client(CHOST, CPORT) # Start up the class for Scratch
scratch.start() # This starts the 'run' function.

server = Server(SHOST, SPORT) # Start up the class for Server
server.start() # This starts the 'run' function on the class as well.

while running:
    time.sleep(THREADCHECK) # The longer the wait time, the less CPU is used I think.
    try: # Check if the either thread died ( or exists anymore ).
        if scratch: pass
        if server: pass
    except: # If either are dead, end the program.
        running = 0
        print "Finished running."

You can learn how to create the files and run them here.

A possibility to have clients connect to the server is create a .exe or .app that they can just download, double-click type in the IP and port to the server they want to connect to, and then they are connected.

Any thoughts?

Edit: Forgot to mention how to set and get variable values... Whoops. xD

You set variables by sending a broadcast '<variable_name value1 value2 etc'.
You get variables by sending a broadcast '>variable_name' then you look for the variable in the sensor value block.
And to follow a variable you broadcast '^variable_name'. What following a block does is automatically update the variable you want to follow. So if you follow a variable called 'hello' and when another person updates that variable to 'world', it sends the update to the client and it updates the variable. And the sensor value block now shows 'hello' and when you display it's value, it will say 'world'.

Note that the client does not continually ask for the updated variable when you follow a variable, it's the server that sends the update when it is updated.  smile

Last edited by Magnie (2012-03-10 09:40:38)

Offline

 

#2 2011-12-22 19:32:34

flashgocrazy
Scratcher
Registered: 2011-01-12
Posts: 500+

Re: Python Network Mirror and Scratch Live Clone

cool. Do you know how to set it up on a website. And what hosting service I should use for it?


◕‿◕

Offline

 

#3 2011-12-22 19:34:30

Magnie
Scratcher
Registered: 2007-12-12
Posts: 1000+

Re: Python Network Mirror and Scratch Live Clone

flashgocrazy wrote:

cool. Do you know how to set it up on a website. And what hosting service I should use for it?

The whole purpose of it is not to host it on a website. Find a Dedicated Server or Shell.

Offline

 

#4 2011-12-22 19:40:27

flashgocrazy
Scratcher
Registered: 2011-01-12
Posts: 500+

Re: Python Network Mirror and Scratch Live Clone

Magnie wrote:

flashgocrazy wrote:

cool. Do you know how to set it up on a website. And what hosting service I should use for it?

The whole purpose of it is not to host it on a website. Find a Dedicated Server or Shell.

where do I find the dedicated server or shell?


◕‿◕

Offline

 

#5 2011-12-22 19:47:10

Magnie
Scratcher
Registered: 2007-12-12
Posts: 1000+

Re: Python Network Mirror and Scratch Live Clone

flashgocrazy wrote:

Magnie wrote:

flashgocrazy wrote:

cool. Do you know how to set it up on a website. And what hosting service I should use for it?

The whole purpose of it is not to host it on a website. Find a Dedicated Server or Shell.

where do I find the dedicated server or shell?

You search for it. I suggest using a Linux computer for while before hosting anything.

Offline

 

#6 2011-12-22 19:47:45

ohaiderstudios
Scratcher
Registered: 2010-10-31
Posts: 100+

Re: Python Network Mirror and Scratch Live Clone

flashgocrazy wrote:

Magnie wrote:

flashgocrazy wrote:

cool. Do you know how to set it up on a website. And what hosting service I should use for it?

The whole purpose of it is not to host it on a website. Find a Dedicated Server or Shell.

where do I find the dedicated server or shell?

Here
or
Here


Fork Clamor on GitHub!

Offline

 

#7 2012-03-08 18:17:15

Dudblockman
Scratcher
Registered: 2011-08-02
Posts: 2

Re: Python Network Mirror and Scratch Live Clone

I think the client is broken. I downgraded my python to 2.7 (I hope it's the right one) and ran the program. This error popped up:
Traceback (most recent call last):
  File "C:\Users\Dell\Desktop\SMClient.py", line 57, in <module>
    class Server(threading.Thread, SHOST, SPORT): # This class listens for messages from the Server and sends it to Scratch.
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I tried to fix this, but I know nothing about meta-classes, and the internet was no help.

Offline

 

#8 2012-03-08 22:35:03

Magnie
Scratcher
Registered: 2007-12-12
Posts: 1000+

Re: Python Network Mirror and Scratch Live Clone

Dudblockman wrote:

I think the client is broken. I downgraded my python to 2.7 (I hope it's the right one) and ran the program. This error popped up:
Traceback (most recent call last):
  File "C:\Users\Dell\Desktop\SMClient.py", line 57, in <module>
    class Server(threading.Thread, SHOST, SPORT): # This class listens for messages from the Server and sends it to Scratch.
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I tried to fix this, but I know nothing about meta-classes, and the internet was no help.

Ah, sorry. I've added the fixed code to the main post.

SHOST and SPORT were in Server() and not __init__() in case you were wondering. I know nothing of meta-classes either. xD

Last edited by Magnie (2012-03-08 22:36:54)

Offline

 

#9 2012-03-09 19:01:32

Dudblockman
Scratcher
Registered: 2011-08-02
Posts: 2

Re: Python Network Mirror and Scratch Live Clone

Magnie wrote:

Dudblockman wrote:

I think the client is broken. I downgraded my python to 2.7 (I hope it's the right one) and ran the program. This error popped up:
Traceback (most recent call last):
  File "C:\Users\Dell\Desktop\SMClient.py", line 57, in <module>
    class Server(threading.Thread, SHOST, SPORT): # This class listens for messages from the Server and sends it to Scratch.
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I tried to fix this, but I know nothing about meta-classes, and the internet was no help.

Ah, sorry. I've added the fixed code to the main post.

SHOST and SPORT were in Server() and not __init__() in case you were wondering. I know nothing of meta-classes either. xD

I figured it out after a bit, I checked here today and... you had the same solution!
I was about to post my mistake and solution, but you beat me to it.

Good job by the way, I am using this to explore networking. I think it is a good idea to understand it here before I even attempt to do it in Java or Action Script. (Yeah I can do that!)

Offline

 

Board footer