Skip to main content
WorkProjects

MazeWar Game Server

Multi-threaded real-time combat in C

stable
View raw

A multi-threaded C server for real-time maze combat. One thread per TCP client, a shared maze guarded by fine-grained locks, a custom binary packet protocol, and SIGUSR1-driven asynchronous laser-hit notification. Coursework, written to be Valgrind-clean and Criterion-tested end to end.

What it is

MazeWar is a 1980s-era multiplayer game in which players navigate a shared maze, see each other through a first-person view, and shoot lasers. This repository is the server side: a TCP daemon that accepts client connections, maintains the maze and player state, and pushes incremental view, scoreboard, and chat updates back to every client. Built for CSE 320 (Systems Fundamentals II) at Stony Brook, Apr-May 2025. Headers marked === DO NOT MODIFY THIS FILE === are the course-provided contract; everything under src/ is the implementation.

By the numbers

MetricValue
C source files6 (~1,616 LOC under src/)
Header files6 (~861 LOC under include/)
Criterion test suite145 lines
Packet types13 (6 client-to-server, 7 server-to-client)
Packet header16 bytes fixed + optional length-prefixed payload
Concurrency model1 service thread per connected client
Compiler flags-Wall -Werror, links -lpthread -lcurses

Architecture

                  +------------------+
   SIGHUP ------> |   main thread    |  accept() loop on listenfd
                  +------------------+
                           |
                           | pthread_create per connection
                           v
         +-------------------------------------+
         |   client service threads (N)        |
         |   mzw_client_service(int *fd)       |
         |   - proto_recv_packet               |
         |   - dispatch: LOGIN/MOVE/TURN/FIRE/ |
         |     REFRESH/SEND                    |
         |   - proto_send_packet               |
         +-------------------------------------+
           |            |               |
           v            v               v
   +----------------+ +--------+  +---------------+
   | CLIENT_REGISTRY| | PLAYER |  |     MAZE      |
   | mutex + sem    | | table  |  | lock per cell |
   | wait_for_empty | | refcnt |  | avatar coords |
   +----------------+ | recmtx |  +---------------+
                      +--------+
                          ^
                          | SIGUSR1 on laser hit
                          |
                     shooter thread
  • Client registry (src/client_registry.c) — registers fds, signals main when empty, issues shutdown(2) on every fd during teardown.
  • Protocol (src/protocol.c) — proto_send_packet / proto_recv_packet handle htonl/ntohl and payload framing over a TCP byte stream.
  • Maze (src/maze.c) — parses the template, tracks which cell each avatar occupies, performs collision and line-of-sight queries.
  • Player (src/player.c, ~600 LOC) — login/logout, scoreboard, view diffing, laser resolution, chat broadcast, recursive locking discipline.
  • Server loop (src/server.c) — per-client service function driving the protocol state machine.

Key features

  • POSIX threads, one per client. main() accepts on the listening socket and hands each connection to mzw_client_service() running in a detached thread.
  • Thread-safe client registry. creg_register / creg_unregister track live file descriptors. creg_wait_for_empty blocks the main thread during shutdown until every service thread has exited.
  • Reference-counted player objects. Players can be referenced concurrently by the maze module and by other players' view updates; refcounts keep them alive until the last reference drops.
  • Recursive mutexes on players. Player -> maze -> player_update_view up-calls require PTHREAD_MUTEX_RECURSIVE to avoid self-deadlock.
  • Mutexes plus semaphores. Mutexes protect shared state; semaphores coordinate the empty-registry wait during graceful shutdown.
  • Custom binary protocol. Fixed 16-byte header in network byte order (type, 3x int8 params, uint16 payload size, uint32 sec / uint32 nsec timestamp) followed by an optional payload.
  • SIGUSR1 for async laser hits. When a player is shot, the shooter's thread signals the victim's thread with SIGUSR1 to interrupt any blocking read and trigger alert delivery without polling.
  • SIGHUP for graceful shutdown. The main thread closes the listening socket, shuts down every registered client fd, waits for the registry to drain, then exits.
  • Valgrind-verified. No leaks, no invalid reads/writes under the standard course test battery.

Stack

LayerTechnology
LanguageC (gnu11), -Wall -Werror
ConcurrencyPOSIX threads, mutexes (incl. PTHREAD_MUTEX_RECURSIVE), counting semaphores
NetworkingBSD sockets, TCP/IPv4
SignalsSIGHUP (shutdown), SIGUSR1 (async laser notification)
ToolingGNU Make, Valgrind, Criterion, ncurses (reference clients)