Hello,
This is my first time in the forum so bear with me here.
I’m attempting to make a very simple server-client game in the bge. Whenever I try to write to an object’s worldPosition property in any thread other than the main thread, it yields an access violation error. I have tried to use queues and mutexes to no avail. I have to be able to write to it from any thread. Any help would be appreciated.
First, bge isn’t thread safe. That’s also what you found out.
Second why would you need threads? You can have a socket connection without a thread, just have it be non blocking.
Third, you can have threads that write to game objects, kinda. (yay, contradicting myself)
The stupid way was, if i remember correctly, to have a while true loop with sleep where you try/except to import bge.
Basically hopping to hit the time frame where the logic tic is.
I need threads because there could be an arbitrarily long delay between when a user joins and a username is set(no duplicate usernames). I am just wondering if there is a way to set a property of an object from any other thread.
The sample code using threads that you often find in the internet, does not apply to the BGE and should not be used here.
You do not need threads. Threaded communication is already setup inside the socket library. It does not need to access the BGE and therefore it causes no conflicts with it.
When you use non-blocking mode your game continues, but checks at each single frame if there is something new from the socket. This check will occur at the right moment (when the python controller gets executed). This ensures you can safely access the BGE.
Basically, write a script that checks the socket. If something is there, process it, and exit the controller. When nothing is there, exit the controller immediately. Make sure the controller gets triggered once each frame (enable True Level Triggering).
Still working on the script. Should I use getCurrentScene() to reference an object(since it’s a function), or should I store it in a variable and then reference the object?
basictest.blend (488 KB)
Now when I run the script, Blender freezes. In the attached file, the script is meant to run once in an infinite loop in the main thread. Is that what’s causing the issue? In the example, the current client is supposed to be a cube, and all other clients a sphere.
Another question, if I have a setup script and a main script running with the true pulse always sensor, are variables(like sockets) kept in a state where I can still use them in the main script?
UPDATE: Okay, I’ve almost got it working. The script now is fully non-blocking and runs over and over with the always sensor, rather than just once in an infinite while loop. I need to be able to somehow save the socket variable such that it won’t be lost between two script runs.
Yes! Yes Yes Yes Yes Yessss! I finally got it to work!:D:D:D I ‘saved’ the socket variable from script to script by utilizing bge.logic.globalDict. Thank you for the suggestions, otherwise I would still have been keeping the script in an infinite while loop, freezing Blender.
For future readers:
Make sure your script never runs in a while true loop, otherwise Blender freezes while the bge waits for the script to end.
To save a variable from script-to-script pass, use bge.logic.globalDict[‘somevar’]=myvar
Always .decode() what you .encode() and .loads() what you .dumps(). I have made that mistake many times.
Again, many thanks for the suggestions.
Sincerely,
Bub
Why not simply store it in your own module?
import socket, bge
mySocket = None
def openConnection():
global mySocket
...
mySocket = socket.Socket(...)
def processReceived():
mySocket.read...
Would that be faster/more efficient than using bge.logic.globalDict?
It will not be faster (why should it be? it is still a module).
In my eyes it makes much more sense. bge.logic.globalDict has a completely different purpose (unfortunately the name does not reflect that). It is the input of the save actuator and the output of the load actuator. When you use it that way it is like using your car as storage for your food. It is possible. Do you think it is a good usage?
Okay, I’ll see what I can do. I didn’t know that the dictionary was for file loading. They should have named it globalFileBuffer or similar. I also thought that declaring a variable global meant that that it was universally accessible, but that the memory was only preserved for one script pass.
Make sure you use a looping construct (e.g while) to receive all messages from the socket until it throws a socket error, then break. Otherwise you’ll receive older-and-older messages if you poll the socket less frequently than it receives (a common problem)
Almost there. There’s still a few bugs, most notably not seeing some clients when you have 4+ clients(the server is a client as well). I haven’t yet gotten to declaring the socket global yet, but that’s a minor issue.
Here’s my blend:
untitled.blend (484 KB)
I suggest to split the code much more. Just now it looks like it tries to rules the world.
A) The camera contains network logic. Isn’t it better when a separate objects focus on such operations? I mean cameras are expected to do … camera thingies. Establishing network connections seem not to fit really well.
B) I suggest to use module mode for bettwer isolation of the various network operations (listening, connect, disconnect, send …).
C) Nested function definitions make the code harder to read. How does it look when these function definitions are not inside another function? How does it look like when these function definitions are encapsulated into a separate module (and then imported)?
D) Short variable names are nice, within a very close context. Wouldn’t it be more comprehensible when the variable has a meaningful name? When a variable name can easily lead to misunderstandings (short names always fit do that category) wouldn’t it be better to see how it is assigned without the need to page up and analyse code?
E) You still have a thread. As written above, this is not the best idea within the BGE.
F) The endless while loop is dangerous as it has a high potential to hold the BGE game loop. How about a security check that ensures the processing does not exceed a certain time frame?
G) Are you sure you want to slow down the processing in a nearly-realtime environment via sleep()? The idea of non-blocking is that you read all messages that are already received rather than to wait for more. Therefore there is no need to wait.
H) Wouldn’t your network code easier to maintain, when you isolate it from command processing (formatting and parsing commands)? I mean it is not very likely that your network code will be extended in future. But it is very likely that you will introduce new commands.
The only reason I am still importing thread, mutex, and sleep is because when a user leaves(msg=0), the dictionary(clients) size cannot change in a loop without yielding an error. I started a thread that takes a mutex to avoid that error. I imported sleep to give the thread time to claim the mutex.
I’ll try to rewrite the code later when I have time.
As far as having the code logic one the camera, I will eventually have it such that the object(tank, eventually) can change to any model, as well as having changing maps and lights. So far the only static object is the camera.
Also, really wondering, do bge logic nodes compile down into raw python code on runtime? If not, how did the Blender devs implement it?
Removing items from a list that is being iterated, by using a temporary container.
temp = []
for item in the_thing:
if condition:
temp.append(item)
the_thing = temp
Sorry if I didn’t get the problem, but it sounded like that…
Those are some very good guides. Cleared a few things up for me, considering I did just about everything wrong.:o