Developing A Game Engine with Perl: Part 7 - Fork
Tags: perl, linux, gamedev, devjournal
Published: 2021-12-03 23:34:45

[Pssssst...](https://dev.to/ansigameengine/developing-a-game-engine-with-perl-part-2-mouse-input-43nj) **I DO NOT KNOW WHAT I AM DOING.**

If you want to start reading from the beginning. Check out the [first article in this series](https://dev.to/ansigameengine/developing-a-game-engine-with-perl-3b8)

Continuing from our last post, I talked about how [ANSI Game Engine](https://www.instagram.com/ANSIGameEngine/) is a [colourful telnet server](https://dev.to/ansigameengine/developing-a-game-engine-with-perl-part-6-a-colourful-telnet-server-4i7g). We left off with needing to fork the engines telnet server.

## Player 2 has joined the game!

Time to level up our telnet server and make it multi-player with some knify forky.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pyx9lszlna5k69crhw62.png)

I've added in the [`strftime`](https://man7.org/linux/man-pages/man3/strftime.3.html) identifier from [Perl's POSIX module](https://perldoc.perl.org/POSIX) to help with time stamping the output. The [`setsid`](https://man7.org/linux/man-pages/man2/setsid.2.html) identifier is for starting a new session and group ID for each forked process. A.K.A, the child process. [`:sys_wait_h`](https://perldoc.perl.org/functions/waitpid) is for returning without wait after the child process has exited, using the WNOHANG flag when calling [waitpid()](https://man7.org/linux/man-pages/man2/wait.2.html). This provides non-blocking wait for all pending **zombie** children.

## Zombie Attack!!!
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31gew1fffn1ac0v0zfzn.jpeg)
You see, when a process dies (exits), it becomes a zombie and needs to be **reaped**. This will be done when our parent process calls [waitpid](https://man7.org/linux/man-pages/man2/wait.2.html) after receiving a [CHLD signal](https://man7.org/linux/man-pages/man7/signal.7.html), indicating the child has stopped or terminated.

Ok, I hope that will give you enough information to work with while dissecting the code:

```
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
use POSIX qw(setsid);
use POSIX qw(strftime);
use POSIX ":sys_wait_h";

sub timestamp {
	my $epoc_seconds = time();
	my $time = strftime "%H:%M:%S", localtime($epoc_seconds);
	my $date = strftime "%m/%d/%Y", localtime;
	my $return = $date . " " . $time;
	return ($return);
}

sub logmsg { print timestamp . " -> $0 -> PID:$$: @_ \n" }
logmsg "Begin";

my $socket = new IO::Socket::INET (
    LocalHost => '192.168.1.15',
    LocalPort => '27777',
    Proto => 'tcp',
    Listen => SOMAXCONN,
    ReuseAddr => 1
);

my $waitedpid = 0;
my $player_data;
my $player_socket;

sub REAPER {
	local $!;   # don't let waitpid() overwrite current error
	logmsg "Ending Player's Game";
	while ((my $pid = waitpid(-1, WNOHANG)) > 0 && WIFEXITED($?)) {
		logmsg "Closed Game ID:$pid : WaitPid:$waitedpid : " . ($? ? " with exit $?" : "");
	}
	$SIG{CHLD} = \&REAPER;  # loathe SysV
}
#if we get the CHLD signal call REAPER sub
$SIG{CHLD} = \&REAPER;

logmsg "Ready and waiting for connection";
while(1)
{
    next unless $player_socket = $socket->accept();
    logmsg ("Incomming Connection");
    logmsg ("Spawning Player A Game");
    my $pid = fork();
        
    next if $pid; #NEXT if $pid exists (parent)

    #As Child
    setsid();
    my $proc = $$;
	
	logmsg ("Game ID:$proc -> Ready");

	# get information about a newly connected player
	my $player_address = $player_socket->peerhost();
	my $player_port    = $player_socket->peerport();
	logmsg "Game ID:$proc -> Connection from $player_address:$player_port";

	my $response = "Welcome Player: $player_address:$player_port. Press any key to disconnect.";
	$player_socket->send($response);

	while ($player_socket->connected()) {
		$player_socket->recv($player_data, 1024);
	        if ($player_data) {
	            logmsg "Player Disconnecting $player_address : $player_port";
	            $socket->close();
                logmsg "Player Disconnected";
	            last;
	        }
	}
	last;
}
exit;
```

Running this code and connecting with two players via [SyncTERM](https://syncterm.bbsdev.net/), our [client of choice](https://dev.to/ansigameengine/developing-a-game-engine-with-perl-part-6-a-colourful-telnet-server-4i7g), shows the following:

```
localhost:~/ANSIGameEngine # perl forking_telnet_server.pl 
12/03/2021 18:16:58 -> forking_telnet_server.pl -> PID:15978: Begin 
12/03/2021 18:16:58 -> forking_telnet_server.pl -> PID:15978: Ready and waiting for connection 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15978: Incomming Connection 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15978: Spawning Player A Game 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15979: Game ID:15979 -> Ready 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15979: Game ID:15979 -> Connection from 192.168.1.9:33422 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15978: Incomming Connection 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15978: Spawning Player A Game 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15980: Game ID:15980 -> Ready 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15980: Game ID:15980 -> Connection from 192.168.1.9:33428 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15979: Player Disconnecting 192.168.1.9 : 33422 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15979: Player Disconnected 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15978: Ending Player's Game 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15978: Closed Game ID:15979 : WaitPid:0 :  
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15980: Player Disconnecting 192.168.1.9 : 33428 
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15980: Player Disconnected 
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15978: Ending Player's Game 
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15978: Closed Game ID:15980 : WaitPid:0 :  
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/my89biw6wsyhn0kx7klb.png)


## How it all works

The main (parent) process that accepts new incoming telnet requests is `PID:15978` in the above example. After it sets up the listen server, it waits for a connection request and creates a forked process when a new player connects (child). The code distinguishes the parent (main waiting telnet server) process from the child (player) process with the value [`fork()`](https://perldoc.perl.org/functions/fork) returns. The parent process receives the child's (player) PID as the return value of `fork()`, so it loops back up and waits for another player to connect. The child (player) process receives a value of `0` from `fork()`, so we continue downward in the code. In Perl doing `if($pid)` does **NOT** evaluate `TRUE` if `$pid == (0 || undef)`, which is what the child (player) process will receive as the returned value from `fork()`. We give the child (player) process a new session, record it's PID (`$$`) and wait for them to press any key. When the player presses a key the socket is closed and the child (player) process exists and becomes a zombie. This is when the parent (main) process receives the CHLD signal (`$SIG{CHLD}`) and calls **`REAPER`**

## How about you?

Have you worked with fork before? Have you unleashed a zombie apocalypse forgetting to reap? Comment about your experience, I'd love to hear your stories.

If you have any suggestions or comments please share constructively. Also please visit our social media pages for lots of fun videos and pictures showing the game engine in action. 

[ANSI Game Engine on Instagram](https://www.instagram.com/ANSIGameEngine/)
[ANSI Game Engine on Facebook](https://www.facebook.com/ANSIGameEngine)

[Prev << Part 6 - A Colourful Telnet Server](https://dev.to/ansigameengine/developing-a-game-engine-with-perl-part-6-a-colourful-telnet-server-4i7g)
[Next >> Part 8 - Vim](https://dev.to/ansigameengine/developing-a-game-engine-with-perl-part-8-vim-2gj5)

Cheers!
Shawn