CSCI-611 Project 4: Goldchase–Two tin cans and a string

$35.00

Category:

Description

5/5 - (10 votes)

This project is an enhancement of project 3, where we add support for sockets so players on
other computers can connect to a running game.
Summary
A game instance may be running on either a server computer or a client computer. More than
one game instance may be running on both the client and the server. There may only be one
server computer but there may be multiple client computers.
The first game instance to start on a server computer will also start a daemon on that server.
Likewise, the first game instance to start on a client computer will also start a daemon on the
client computer. The daemons manage the communications between computers. It will also now
be the responsibility of the the daemons to clean up/remove shared memory once all players
have left the game.
Initial game play enhancements
Make three minor modifications to goldchase:
1. Add an additional member to the gameBoard structure:
int daemonID;
2. As in the previous project, assign the game’s process ID to the gameBoard structure in
shared memory.
3. Check the contents of the daemonID variable you added above. If it is not equal to zero,
then send the daemon process a SIGHUP signal.
4. When the game is exiting, after the game’s process ID has been set to zero and the player
removed from the map, send the daemon process another SIGHUP signal. In the previous
implementation of Goldchase, the last player would unlink the shared memory and
semaphore. The daemon will now be responsible for removing the shared memory and
semaphore.
CSCI-611 Project 4: Goldchase–Two tin cans and a string Ver. 1.16, 4/2017
Signal handlers in daemons
SIGHUP (player has joined or left game)
1. Socket write: Socket Player
2. After the socket write, check the value of the byte you just wrote to the socket. If the byte
equals G_SOCKPLR (that is, all of the player bits are off), then there are no more players
playing the game. Close and unlink the shared memory and semaphore, the exit the
program.
SIGUSR1 (map has changed)
1. Create vector pairs
[pair.first=(short)offset, pair.second=(unsigned char)mapsquare]
of each map square which is different between local copy and shared memory. Update local
copy as necessary.
2. If length of vector is > 0, socket write: Socket Map
SIGUSR2 (a message queue message is waiting)
1. Loop through each message queue. When a message is found in queue, socket write:
Socket Message protocol.
Socket communications protocol
Socket Message (a message is being sent to a player)
(1) 1 byte: G_SOCKMSG or’d with G_PLR# of message recipient
(2) 1 short: n, # of bytes in message
(3) n bytes: the message itself
Socket Player (a player is joining or leaving the game)
(1) 1 byte: G_SOCKPLR or’d with all active players G_PLR#
Socket Map (a map refresh is required)
(1) 1 byte: 0
(2) 1 short: n, # of changed map squares
(3) for 1 to n:
1 short: offset into map memory
1 byte: new byte value
Socket Client Initialize (the client daemon has connected)
(1) 1 int: the number of map rows
(2) 1 int: the number of map cols
(3) n bytes: (where n=rows X cols) the map
CSCI-611 Project 4: Goldchase–Two tin cans and a string Ver. 1.16, 4/2017
Daemon behaviors
Server start up
1. Connect to shared memory & map via mmap()
2. Call getpid() to retrieve the daemon’s process id for the daemonID field in the
gameBoard structure in shared memory.
3. Allocate memory (rows * columns) and initialize it with the same values that are in the
shared memory map. The server maintains a local copy of the map so that when it receives
a SIGUSR1, it can compare the two maps to determine which bytes in the shared memory
map were changed by a game process.
4. Start trapping SIGHUP, SIGUSR1, and SIGUSR2
5. Listen on socket for a client to connect
Server receives client connect
1. Socket write: Socket Client Initialize (see Socket communications protocol)
2. Socket write: Socket Player (see Socket communications protocol)
3. Enter an infinite loop. The loop should block on the read() system call (actually, READ),
trying to read a single byte. The contents of that byte will determine which communication is
being started (note that all types of messages under Socket communications protocol
begin with a single byte).
Client startup
1. Read the rows and columns from the socket (see Socket Client Initialize protocol).
2. Allocate space (rows * cols) for a local copy of map (not the gameBoard, just the map)
3. Read n bytes (where n=rows*cols) from the socket into the local copy of map (see Socket
Client Initialize protocol). The client maintains a local copy of the map so that when it
receives a SIGUSR1, it can compare this local copy with the shared memory map to
determine which bytes in the shared memory map were changed by a game process.
4. Create a semaphore
5. Create shared memory (shm_open, ftruncate) just as a new game instance would.
6. Initialize shared memory from your local copy of map, and rows & cols.
7. Call getpid() to retrieve the daemon’s process id for the daemonID field in the
gameBoard structure in shared memory.
8. Start trapping SIGHUP, SIGUSR1, and SIGUSR2
9. Read Socket Player from Socket (handle as per SIGHUP, step 2).
10. Enter an infinite loop. The loop should block on the read() system call (actually, READ),
trying to read a single byte. The contents of that byte will determine which communication is
being started (note that all types of messages under Socket communications protocol
begin with a single byte).
CSCI-611 Project 4: Goldchase–Two tin cans and a string Ver. 1.16, 4/2017
The three headings below correspond to the three possible bytes
which may read from the socket as per the infinite loop #9 above
On socket read “Socket Player”
Loop through player bits. The player bits are contained in that single byte you read from the
socket protocol: Socket Player (a player is joining or leaving the game):
1. If player bit is on and shared memory ID is zero, a player (from other computer) has joined:
1.1. Create and open player’s message queue for read (daemon will receive messages
on behalf of player, forwarding them across socket)
1.2. Set shared memory ID to pid of daemon
2. If player bit is off and shared memory ID is not zero, remote player has quit:
2.1. Close and unlink player’s message queue
2.2. Set shared memory ID to zero
After looping through the player bits, check the value of that single byte you read in. If that
single byte equals G_SOCKPLR (that is, all of the player bits are off), then no players are left in
the game. Close and unlink the shared memory. Close and unlink the semaphore. Then exit
the program.
On socket read “Socket Message”
After reading this first byte, you need to process the remaining bytes from the socket protocol:
Socket Message (a message is being sent to a player).
1. Determine message recipient
2. Determine message
3. Open, write, and close to message queue of recipient
On socket read “Socket Map” (A 0 which is map update)
After reading this first byte, you need to process the remaining bytes from the socket protocol:
Socket Map (which means a map refresh is required)
1. Determine number of changed map squares
2. Change local copy and shared memory copy of map
3. Send SIGUSR1 to each local player
CSCI-611 Project 4: Goldchase–Two tin cans and a string Ver. 1.16, 4/2017
Final game play enhancements
Don’t integrate these additional enhancements until after you have all other aspects of the
application working:
1. If a hostname is provided on the command line, the Goldchase process is assumed to be a
remote process which interacts with a client daemon.
2. If a hostname is not provided on the command line, the Goldchase process is assumed to
be a local process which interacts with a server daemon.
3. After a Goldchase local process has registered its process ID with shared memory, check
the daemonID.
1. If the daemonID is not zero, send a SIGHUP signal to the daemon process (this was
completed as part of the initial enhancements).
2. If the daemonID is zero, fork and exec the server daemon.
4. Before a Goldchase remote process initializes (immediately after startup),
1. if the shm_open() command fails, then the client daemon isn’t running yet. Fork and
exec the client daemon. Be sure to provide the client daemon with the process ID of the
parent process (the game is the parent, the new client daemon is the child). Once the
client daemon is initialized and ready, it should signal its parent that it may continue
initializing The parent should proceed as in #2 below.
2. If the shm_open() command succeeds, the the client daemon is already running.
After the remote process has registered its process ID with shared memory, send a
SIGHUP signal to the client daemon (this was completed as part of the initial
enhancements).
5. When a Goldchase local process or remote process quits, send a SIGHUP signal to the
daemon process (this was completed as part of the initial enhancements).