This is my own attempt of making online game creation easier for those who are only logic brick savvy. There are two parts to this: a python module and a collections of python scripts for easing the burden of developing online games in blender.
Python Module
The first part of what I’m releasing is a python module that I wrote a while ago; its purpose is to decrease the development time necessary for creating a server and client. Its name is bi_net and it contains two classes.
The first class is Server. It’s purpose it to act as a server. The class is meant to be extended for full use. The on_input, on_output, on_connect, and on_disconnect methods are what should be overridden.
The second class is Client. It acts as a client. Just like the Server class, it needs to be extended for full use. The on_input and on_disconnect methods are what should be overridden.
I did do some rather odd, maybe even unpytohnic things in the module, but fortunately you’ll find examples in the next section.
import socket, time
"""A module for performing socket input and output with less work.
This module offers a simple way of using a UDP socket. There
is only support for IP, and the classes are best used as
superclasses.
"""
class Server:
"""Class for receiving input and output from multiple clients.
The constructor takes no arguments. Instead is checks if IP,
PORT, BUFF_SIZE, or TIMEOUT have been defined in self, if not, they are
given default values.
"""
def __init__(self):
"""Constructor. See class doc string."""
if not ('IP' in self.__dict__):
self.IP = socket.gethostbyname(socket.gethostname())
if not ('PORT' in self.__dict__):
self.PORT = 21500
if not ('BUFF_SIZE' in self.__dict__):
self.BUFF_SIZE = 1024
if not ('TIMEOUT' in self.__dict__):
self.TIMEOUT = 0.005
if not ('DISCONNECT' in self.__dict__):
self.DISCONNECT = 30
self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server.bind((self.IP, self.PORT))
self.server.settimeout(self.TIMEOUT)
self.clients = []
self.client_times = []
def recv(self):
"""Returns all packets that have been received.
Each time a packet is succesfully received, the on_input
method is called.
If an error has occured while receiving a packet, it is
assumed that a client has disconnected.
Once a client has disconnected, they are removed from the
self.clients list, and the on_disconnect method is called.
Whenever a new client connects, their address is added to
the self.clients list, and the on_connect method is called.
"""
concatenated = ''
data = bytes()
addr = tuple()
while 1:
for client in self.clients:
index = self.clients.index(client)
difference = time.time() - self.client_times[index]
if (difference > self.DISCONNECT):
self.on_disconnect(client)
del self.clients[index]
del self.client_times[index]
try:
data, addr = self.server.recvfrom(self.BUFF_SIZE)
except socket.timeout:
return concatenated
except socket.error:
continue
data = data.decode('utf-8')
if not (addr in self.clients):
self.clients.append(addr)
self.client_times.append(time.time())
self.on_connect(addr)
self.client_times[self.clients.index(addr)] = time.time()
data = self.on_input(data, addr)
concatenated += data
def send(self, data):
"""Sends data to each client.
Every time, before data is sent to a client, the on_output
method is called.
"""
if hasattr(data, 'encode'):
data = data.encode('utf-8')
for client in self.clients:
output = data
output = self.on_output(output, client)
self.server.sendto(output, client)
def on_input(self, data, address):
"""Runs when a new packet of data is received."""
return data
def on_output(self, data, address):
"""Runs each time a new packet of data is sent."""
return data
def on_connect(self, address):
"""Runs each time a new client has connected."""
pass
def on_disconnect(self, address):
"""Runs when a client has disconnected."""
pass
class Client:
"""Class for receiving input and output from a server.
The constructor takes no arguments. Instead is checks if IP,
PORT, BUFF_SIZE, or TIMEOUT have been defined in self, if not, they are
given default values.
"""
def __init__(self):
"""Constructor. See class doc string."""
if not ('IP' in self.__dict__):
self.IP = socket.gethostbyname(socket.gethostname())
if not ('PORT' in self.__dict__):
self.PORT = 21500
if not ('BUFF_SIZE' in self.__dict__):
self.BUFF_SIZE = 1024
if not ('TIMEOUT' in self.__dict__):
self.TIMEOUT = 0.005
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.connect((self.IP, self.PORT))
self.socket.settimeout(self.TIMEOUT)
def recv(self):
"""Returns all packets that have been received.
Each time a packet is succesfully received, the on_input
method is called.
If an error has occured while receiving a packet, it is
assumed that a server has disconnected.
Once the server is no longer connected the on_disconnect
method is called.
"""
while 1:
input = ''
data = bytes()
try:
data = self.socket.recv(self.BUFF_SIZE)
except socket.timeout:
return input
except socket.error:
self.socket.close()
self.on_disconnect()
break
data = data.decode('utf-8')
data = self.on_input(data)
input += data
def send(self, data):
"""Sends data to the server."""
if hasattr(data, 'encode'):
data = data.encode('utf-8')
self.socket.send(data)
def on_disconnect(self):
"""Runs once the server is not detected."""
pass
def on_input(self, data):
"""Runs each time a packet of data is received."""
return data
Python Scripts
This was an attempt of mine to make online game creation easier for those who are only able to use logic bricks. It may seem daunting, but as long as you follow the directions bellow you’ll be fine.
default.blend
- Make a new blend file and empty the scene.
- Add one object, name it _resource, and move it to layer two.
- Add an empty, name it _spawn, and position it where you want your clients to appear when they connect.
- Add any other objects that you wish to, such as lights or cameras, but be sure that their names begin with an underscore.
- Add the following python scripts into your blend file: client_client, client_input, and server_server.
- Save what you have done so far and name it default.blend.
server.blend
- Reopen blender and append all of the objects and python scripts from default.blend into the current scene.
- Select an object, I recommend a camera, and attach an always sensor with true level triggering selected.
- Add a new controller and set the controller type to python.
- Set server_server as the python script to be ran in the controller.
- Go to layer two, you should see that object named _resource.
- Add a property sensor.
- To test if the client is pressing the w key, you would enter w as the property and 1 as the value to be tested.
- Add an and controller.
- Attach the property sensor and the and controller.
- Add any actuators that you want to the controller.
- Continue steps 6 - 12 until you have configured your clients’ controls.
- Save what you have done as server.blend.
client.blend
- Open a new instance of blender and empty the scene.
- Append all of the objects from default.blend into your current scene.
- Find an object, I recommend a camera, and add an always sensor with true level triggering.
- Add a new controller and set the controller type to python.
- Set the script for the controller to client_client.
- Attach the always sensor to the python controller.
- Add a keyboard sensor with true level triggering and all keys selected.
- Name the controller input.
- Add another controller and set the controller type for it to python.
- Set the script for the new controller to client_input.
- Connect the keyboard sensor to the other controller.
- Save what you have done as client.blend.
If everything was done correctly, then start server.blend on one computer, and you can connect to it on as many alternate computers with client.blend as you wish.
I know that those are some pretty heft instructions and I may not have been clear at times, but hopefully you’ll end up with the right result. Any necessary python scripts that I mentions above can be found in the post below.
There is more information in the next post.