Neoflut - a Python Pixelflut Client

Created on:

Pixelflut being played at Eth0
Pixelflut being played at Eth0.

Summary

Neoflut is a Python-based Pixelflut client that allows users to draw images on a shared canvas. In this article, we'll explore the Pixelflut protocol and discuss the implementation of Neoflut, including code snippets and explanations. The project can be found on GitHub.

Understanding the Pixelflut Protocol

Pixelflut is a simple protocol that enables multiple users to draw on a shared canvas by sending commands over a network. The canvas is represented as a grid of pixels, with each pixel having an RGB color value. Users can modify pixel colors by sending "PX" commands followed by the pixel coordinates (x, y) and the new color value (in hexadecimal format).

For example, a Pixelflut command to change the color of the pixel at (10, 20) to red would look like this:

PX 10 20 FF0000

Neoflut's Implementation

Neoflut is a simple Pixelflut client written in Python that reads an image file, converts it to a series of pixel commands, and sends the commands to a Pixelflut server. The client uses the Python Imaging Library (PIL) to process images and the socket library to send commands over the network.

Let's take a closer look at some of the key functions in the Neoflut client:

1. Converting Images to Pixel Commands

The getpixels() function reads an image file, resizes it to fit the canvas, and converts it into a list of pixel commands. The function takes several parameters, including the image path, the size of the canvas, and whether to center or fill the image on the canvas.

Here's an excerpt from the getpixels() function that demonstrates how the image is loaded and resized:

image = Image.open(imagepath)
resized_image = image.resize(canvas)
pix = resized_image.convert('RGB').load()

After resizing the image, the function iterates through the pixels and appends their coordinates and color values to a list. It then randomizes the list to create a more interesting drawing effect:

pixels = []
for x in range(canvas[0]):
    for y in range(canvas[1]):
        r, g, b = pix[x,y]
        pixels.append((x+offset[0], y+offset[1], r, g, b))
random.shuffle(pixels)
return pixels

2. Preparing Pixel Commands for Transmission

The strings() function takes the list of pixel commands generated by getpixels() and converts them into a format suitable for sending to the Pixelflut server. Each command is formatted as a string, with the pixel coordinates and color values separated by spaces:

lines = []
for pixel in pixels:
    line = "PX %s %s %s%s%s\n" % (pixel[0], pixel[1], '%0x' % (2,pixel[2]), '%0x' % (2,pixel[3]), '%0*x' % (2,pixel[4]))
    lines.append(line)
return lines

3. Connecting to the Pixelflut Server

The connect() function establishes a connection to the Pixelflut server using the socket library. It takes the server's IP address and port number as input:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (server_address, port)
print('connecting to %s port %s' % server_address)
sock.connect(server_address)
print('connected')
return sock

4. Sending Pixel Commands to the Server

The send_thread() function sends the list of formatted pixel commands to the Pixelflut server in a continuous loop. This ensures that the image is redrawn on the shared canvas even if other users are drawing on it:

data = ''.join(lines).encode()
while True:
    sock.sendall(data)

5. Configuring and Running the Client

Neoflut uses a configuration file (config.ini) to specify various settings, such as the image file, the Pixelflut server's address and port, and the desired number of parallel connections. The main() function reads these settings, processes the image, and starts the client processes:

configReader = configparser.ConfigParser()
configReader.read('config.ini')
config = configReader['Neoflut']

# Reading the config params

pixels = getpixels(imagepath, screensize, centering, fill, drawsize)
lines = strings(pixels)

if multicon != 0:
    for i in range(multicon):
        offset = (random.randint(0, len(lines)))
        newlines = lines[offset:] + lines[:offset]
        Process(target=send_thread, args=(newlines, server_address, port)).start()
This configuration file format was added by Hydr4bytes in commit f90448f.

Accessing the Full Code on GitHub

While this article provides an overview of the key components of Neoflut, the full code, including additional features and improvements, can be found on the project's GitHub repository. To explore the complete implementation, obtain the latest updates, or contribute to the project, please visit https://github.com/lucanatorvs/neoflut.

Conclusion

Neoflut is a fun and easy-to-use Python Pixelflut client that enables users to draw images on a shared canvas. By leveraging the PIL and socket libraries, it efficiently handles image processing and network communication tasks. With a clear and straightforward implementation, as well as an easily customizable configuration file, Neoflut provides a great starting point for users interested in participating in Pixelflut games or building upon the existing codebase. Don't forget to check out the full code and latest updates on the project's GitHub repository.