Named Pipe Demo

Back

The Goal

This is still an experimental feature. It is for version 1.01.30.

A named pipe allows the Fractal Block World program to communicate to other programs on your computer. Once you give Fractal Block World permission, it can create a named pipe on your computer (which it can read from and write to). Then, other programs on your computer can read from or write to that same named pipe. It is up to you to run those other programs: Fractal Block World will never launch a program on your computer.

Why would you ever want to do such a thing? You could make an experimental mod which allows players to see the position of their friends also playing the game. It could show an icon on the HUD showing the direction and distance to their friends. This could be implemented by the Fractal Block World program communicating to a 3rd party program X on the user's computer (via a named pipe). That 3rd party program could then communicate to some kind of server program Y on another computer.

Disclaimer

Use this feature at your own risk. The concern is you should never download untrusted programs onto your computer and run them. Fractal Block World is not liable for anything detrimental that happens by you downloading software from the internet (or any other source) and running it. Note that Fractal Block World is also not liable for anything bad that happens as a result of using the Fractal Block World game. This feature is not really intended for the general public. It is more intended for modders making experimental mods.

It would make sense to have a policy that 3rd party programs that communicate with Fractal Block World on a user's machine have to be written in a language like Python. That way the community can see exactly what the code is doing.

Allow the Xar Package to Create Named Pipes

If you play the xar package, then in order for it to create named pipes, the user first needs to give permission for this. Note that once permission is given to the xar package, mods that are loaded along with the xar package also have this permission.

Go to
Options → Mods → Permissions

and select the xar package. Then click the yes button to allow the xar package permission to create named pipes.

This Demo Mod and How it Works

Here is the mod that we will discuss. Install this mod in the Input/Packages directory. This is a mod for the xar package.

Open Fractal Block World and make sure you have given the xar package permission to create named pipes. Then, go to

Options → Mods → Select Mods

and select the xar package and add this mod (demo_named_pipe_mod) as a mod.

Now here is the interesting part. Install the python programming language on your computer. You will need to open the terminal of your computer and install a certain python package by running the following command:
pip install pywin32
Read the file
Input/Packages/demo_named_pip_mod/Misc/pipe_client.py

and you can see that it is not doing anything suspicious. Then open the terminal of your computer, cd into the directory Input/Packages/demo_named_pip_mod/Misc/, and run the following command:
./pipe_client.py
Now the pipe client is running. Every second, it should print a message to the terminal saying that the client is running and waiting for a certain named piped to be created.

While the pipe client is running, open up the Fractal Block World program. Start a new game or load an existing game that uses the xar package. Because you have selected the demo_named_pipe_mod mod as a mod for the xar package, as soon as you load the game, it will create a certain named pipe. If you now look at your computer's terminal, the python pipe client will say that it "got a handle to a pipe".

Now in Fractal Block World, open the game's terminal and run the command "chat carrot". This will send the message "carrot" to the named pipe. If you go back to your computer's terminal, the python pipe client will show that it received the message "carrot".

As soon as the python pipe client received a message from the pipe, it sends the message "potato" back. Look at the Fractal Block World terminal to see that is got the message.

How the Mod Works

There is really only one interesting file in the mod we are talking about, and it is the file

Game/Commands/game_cmd_chat.lua
Let us look at the file:
--File: Game/Commands/game_cmd_chat.lua

--This is called by the engine when the game is loaded.
--It has the effect of registering the "chat" command
--to the command system.  Note that the command system
--is part of the xar package.
function p.__load_game()
    local cmd_name = "chat"
    game_command_system.add_command(cmd_name, p.handler)
    game_command_system.add_help(cmd_name, p.get_help_str())
    --
    --Global variables that start with "temp"
    --are not saved when we save the game.
    ga_init_i("temp.chat_named_pipe_handle", -1)
    local handle = ga_open_named_pipe("FBW_example_pipe")
    ga_set_i("temp.chat_named_pipe_handle", handle)
end

function p.handler(str)
    local handle = ga_get_i("temp.chat_named_pipe_handle")
    ga_write(handle, str)
end

function p.__update_passive()
    p.update_common()
end

function p.__update()
    p.update_common()
end

function p.update_common()
    local handle = ga_get_i("temp.chat_named_pipe_handle")
    local msg = ga_read(handle)
    if( msg ~= "" ) then
        ga_console_print("Msg from named pipe: " .. msg)
    end
end

function p.get_help_str()
    return
           "Usage: chat str\n\n"
        .. "Sends the given string to a certain \"named pipe\". "
end
So the __load_game function is called when the package loads. It registers a new command called "chat" with the xar package command system. That is, the file game_command_system is part of the xar package.

The __load_game function then does something non-trivial: It opens a named pipe with the name "FBW_example_pipe". The full operating system name for the named pipe is slightly different (see the python pipe client). There is an extra rule: the name of the pipe must start with "FBW_".

The function ga_open_named_pipe returns an integer "handle" to the named pipe that was created. If it was not successful, it will return -1 and to the console will be printed a message saying that the xar package does not have permissions to create named pipes.

Here we store the integer handle for the pipe in the variable "temp.chat_named_pipe_handle". Note that variables that start with "temp" are not saved when we save the game.

Then, when the player opens the console and runs the command "chat carrot", then the function game_cmd_chat.handler will be called with the string argument "carrot". This will result in the function ga_write being called, given "carrot" as its string parameter.

This is it for the Fractal Block World part of the mod!

The Python Pipe Client

We have included the python pipe client as the file demo_named_pipe_mod/Misc/pipe_client.py. This is a python program (so you need to have python installed on your computer to run it). Here is the code of this python program:
#!python

#This was partially taken from the following link:
#https://stackoverflow.com/questions/48542644/python-and-windows-named-pipes

import time
import sys
import win32pipe, win32file, pywintypes

print("FBW Named pipe client demo")

while True:
    try:
        pipe_name = r'\\.\\pipe\\FBW_example_pipe'
        handle = win32file.CreateFile(
            pipe_name,
            win32file.GENERIC_READ | win32file.GENERIC_WRITE,
            0,
            None,
            win32file.OPEN_EXISTING,
            0,
            None
        )
        print("Got a handle to a pipe!!! Waiting for data from the pipe.")
        while True:
            buffer_size = 4096
            (hr, byte_data) = win32file.ReadFile(handle, buffer_size)
            print("Got data!")
            str_data = byte_data.decode("utf-8")
            print("Received data: " + str_data)
            #
            print("Writing the following message back to the pipe: potato")
            return_msg = "potato"
            return_bytes = return_msg.encode("utf-8")
            win32file.WriteFile(handle, return_bytes)
    except pywintypes.error as e:
        if e.args[0] == 2:
            print("No pipe, trying again in a sec")
            time.sleep(1)
        elif e.args[0] == 109:
            print("Broken pipe")