Threading question

Well, I feel awkward to have two posts so close to each other on the forums, but this is a different topic on the same project, so it didn’t feel right to just tag it on the other thread.

Now that I have learned TCP is the way to go for my game it appears that a threading system is going to need to be set up. I have never used threads to do anything productive, so I’m a novice at it when it gets complex. I have my code setup the way I want it in my server and I have threading done (to my knowledge) correctly.
Here is what I have: (not cleaned up or optimized)


import sys
sys.path.append('C:\Python32\Lib\site-packages')
import MySQLdb as mdb
import socketserver
import threading
import datetime

maxPly = 20
con = None
cur = None
plyTable = [ [ 0 for i in range(5) ] for j in range(maxPly) ]
curPly = 0

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The RequestHandler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        global curPly
        global maxPly
        global plyTable
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        sent = str(self.data,'ascii').split(":")
        if (sent[0] == "l"):
            #login check
            cur.execute("SELECT password, userid FROM `prototb` WHERE username = '"+sent[1]+"'")
            passW = cur.fetchone()#get the password from the database
            if (passW != None):#if the password doesnt exist the username is wrong
                if (passW[0] == sent[2]):#check the correctness of the password
                    if (curPly < maxPly):#if the server is not over capacity
                        for i in range(maxPly):#add them to the player list
                            if (plyTable[i][3] == False):
                                plyTable[i][3] = True#players active
                                plyTable[i][1] = self.client_address[0]#players address
                                plyTable[i][2] = sent[1]#players username
                                plyTable[i][4] = passW[1]#players id
                                self.request.send(bytes("+:"+str(i), 'ascii'))#allow the request
                                print("sent: "+"+:"+str(i))
                                curPly+=1#add 1 to the player count
                                break
                    else:
                        self.request.send(bytes("ServerFull", 'ascii'))#tell that the server is full
                else:
                    self.request.send(bytes("-", 'ascii'))#refuse entry (bad password)
            else:
                self.request.send(bytes("-", 'ascii'))#refuse entry (bad username)
        elif (sent[0] == "m"):
            print(sent[1])
            cur.execute("SELECT `displayname`,`curmap`,`curpos`,`Inventory`,`abilities` FROM `prototb` WHERE `userid` = '"+str(plyTable[int(sent[1])][4])+"'")
            info = cur.fetchone()#get the info from the database
            infostr = ""
            for i in range(5):
                infostr += (info[i] +":")
            self.request.send(bytes(infostr, 'ascii'))#send the info to the client
            
class ThreadClass(threading.Thread):
    def run(self):
        global cur
        global con
        now = datetime.datetime.now()
        print("Server Base Running: %s"% now)
        HOST, PORT = "localhost", 9001
        print("0")
        con = mdb.connect('localhost', 'root', 'toor', 'protodb');#login to the database
        print("1")
        cur = con.cursor()
        print("2")
    
        # Create the server, binding to localhost on port 9999
        server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
        print("3")
    
        # Activate the server; this will keep running until you
        # interrupt the program with Ctrl-C
        print("4")
        server.serve_forever()
        print("5")
    
if __name__ == "__main__":
    t = ThreadClass()
    t.start()

You will notice a bunch of prints that are in the run function in the ThreadClass. I did this because I noticed a odd phenomena in the code. When I run this code outside blender it works fine and I get the numbers 1-4 printed out, as expected.
EDIT**
Inside blender it doesn’t get past


print("Server Base Running: %s"% now)

If you put a print right under the above line, it won’t print. I have removed the time-stamp and still have the same problem.

-Note: After playing with it, it seems to be random where it stops, sometimes it gets to other numbers. So I think something is terminating it before it can finish.


I have never had code stop mid-stream before so I do not know how to debug/treat this.

Any insight is always welcomed.

P.S. My original plan was to use multiprocessing but I got weirder reactions when I tried to implement that. The code produced a great deal of errors and it opened up another blender-editor program, which when exited printed “Blender Game Ended” in the main file’s terminal. I laughed that off and decided threading is a more productive way to go.

Please excuse my ignorance, but are you sure your code is starting a new thread?

I’ve always known it to be more like:


thread.start_new(func, (args,))

-edit-
Oh yeah… from personal experience, try to end your threads before finishing the game engine simulation, otherwise you’ll crash Blender. It works fine as a runtime when you leave them running, presumably because Windows cleans up all associated threads when it closes.

Both methods of threading have the same outcome. I put t.join() after my current code and now it completes and starts the server; however, the blender window freezes up and doesn’t update. I still have the ability to update the objects positions in my thread but it doesn’t allow logic blocks to run or for any physics to happen.

Sounds like you’re blocking on the same thread as Blender.

As a user of Blender, I’m going to advise against threading, at least within Blender. The problems that can arise from modifying the internal tick based state outside a tick based environ is quite … cumbersome, if not “dangerous” in terms of gameplay. I would avoid threading in the BGE wherever possible.

How would you suggest running a tcp server off blender without using threads?

In my project I wrote a class that launched (and managed) threads to deal with incoming and out going data, but it was the main thread in Blender itself that actually did the changes to objects/variables/etc. Simply put, the threads were recording the data into self.variables and Blender was then monitoring those variables.

I’m thinking of going a more modular route. Setting it up like this will allow me, down the road, to split it up across many computers to run the server. This is easier to program for me, but I think a drawback might be time between client and map server. I think I can make this system work smoothly. On a local network I’m sure 0.003 per message shouldn’t be too much to cause a clog.
Here is a rough image of my idea for a server system:


The first server bubble is a python console running without blender. Then it sends the proper messages to blender game engines, each running a different map, using udp. It is calculated there and send the proper response back to the console server before getting shipped back to the client.
The python console server is the one that will talk to the sql server and bridge data to the game engines.
The game engines will just calculate movement and actions.
Any thoughts on this?

It looks difficult to set up and unnecessarily complex considering the amount of users you can realistically expect to be using the server. If you’re doing the movement on the 'blender map’s and then updating the client with it then they’ll see a lot of input lag.

This is more large scale than what is needed (which makes it wasteful), I agree. But it is my last option as threading is not friendly with blender nor is multiprocessing. I really want my outgoing internet protocol to be tcp, so I see this as a last option. I will just have to spend more time in the optimization of my packets, which should be educational at the least. Applesauce :smiley:

You said in the other (forum) thread you wanted to run Blender on the server for anti-cheating physics… why not just set guidelines about what an acceptable position update is, then let the client update you with its position? E.g. no movement beyond 5BU per second. That way you don’t have to run the Blender simulation on the server and can stick to just being pure python with threads. It would also remove the need for map servers and any input lag.

That was apart of my original plan. It seems simple and the idea is relatively hack proof. The difference comes with severe deconstruction of the idea. The problem is it gets slower and slower as you add more things to your game.
An example is jumping or magic/health bars.

Jumping-they could hack to allow flying
To stop this you would need rules to time their jumps, which is flawed because what if they jump off a cliff. Or if there is a spell to fly there is no way to determine if they aren’t using that spell, because they tell you what they are using.

magic/health bar-never depletes
The server would have to map every attack against them and make sure their health goes down. When you add armor to the game you have to send that info to the server as well (which would have to be cross referanced to a database of armor so they don’t hack the defense). When you add health potions it is even more complex.

All these rules and checks for every player would slow it down.