diff --git a/cmd/client/main.go b/cmd/client/main.go
index 11b1bda..6bd00ac 100644
--- a/cmd/client/main.go
+++ b/cmd/client/main.go
@@ -1,95 +1,107 @@
 package main
 
 import (
-	"bufio"
 	"fmt"
+	"io"
+	"log"
 	"os"
+	"sshpong/internal/client"
 	"sshpong/internal/netwrk"
-	"strings"
+
+	"google.golang.org/protobuf/proto"
 )
 
+var exit chan bool
+
 func main() {
-
-	lobbyChan := make(chan netwrk.LobbyPlayerStatus)
-	interrupter := make(chan netwrk.Interrupter)
-	messageOutput := make(chan *netwrk.LobbyMessage)
-	inputChan := make(chan string)
-
 	fmt.Println("Welcome to sshpong!")
 	fmt.Println("Please enter your username")
 
-	go func() {
-		scanner := bufio.NewScanner(os.Stdin)
-		for scanner.Scan() {
-			text := scanner.Text()
-			inputChan <- text
-		}
-	}()
-
-	reader := bufio.NewReader(os.Stdin)
-	username, err := reader.ReadString('\n')
-	if err != nil {
-		fmt.Println("Error reading from your shit bro...")
-	}
-
-	go netwrk.ConnectToLobby(username, messageOutput, lobbyChan, interrupter)
+	egress := make(chan *netwrk.LobbyMessage)
+	ingress := make(chan *netwrk.LobbyMessage)
 
 	buf := make([]byte, 1024)
+	n, err := os.Stdin.Read(buf)
+	if err != nil {
+		log.Panic("Bro your input is no good...")
+	}
+	username := string(buf[:n])
 
-	for {
-		select {
-		case msg := <-interrupter:
-			fmt.Println(msg.Message)
-		default:
-			n, err := os.Stdin.Read(buf)
-			if err != nil {
-				fmt.Println("Error reading from stdin")
-				return
-			}
-
-			input := string(buf[:n])
-			args := strings.Fields(input)
-			switch args[0] {
-			case "invite":
-				if args[1] != "" {
-					messageOutput <- &netwrk.LobbyMessage{
-						PlayerId: username,
-						Type:     "invite",
-						Content:  args[1],
-					}
-				} else {
-					fmt.Println("Please provide a player to invite ")
-				}
-			case "chat":
-				if args[1] != "" {
-					messageOutput <- &netwrk.LobbyMessage{
-						PlayerId: username,
-						Type:     "chat",
-						Content:  strings.Join(args[1:], " "),
-					}
-				}
-			case "/":
-				if args[1] != "" {
-					messageOutput <- &netwrk.LobbyMessage{
-						PlayerId: username,
-						Type:     "chat",
-						Content:  strings.Join(args[1:], " "),
-					}
-				}
-			case "quit":
-				return
-			case "q":
-				return
-			case "help":
-				fmt.Println("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
-			case "h":
-				fmt.Println("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
-			default:
-				fmt.Println("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
-			}
-
-		}
-
+	conn, err := netwrk.ConnectToLobby(username)
+	if err != nil {
+		log.Panic(err)
 	}
 
+	// User input handler
+	go func(egress chan *netwrk.LobbyMessage) {
+		buf := make([]byte, 1024)
+		for {
+
+			n, err := os.Stdin.Read(buf)
+			if err != nil {
+				log.Panic("Bro your input wack as fuck")
+			}
+
+			userMessage, err := client.HandleUserInput(buf[:n])
+			if err != nil {
+				fmt.Println(err)
+				continue
+			}
+
+			userMessage.PlayerId = username
+			egress <- userMessage
+		}
+	}(egress)
+
+	// Ingress Handler
+	go func(oc chan *netwrk.LobbyMessage) {
+		for {
+			msg := <-ingress
+
+			client.HandleServerMessage(msg)
+		}
+
+	}(ingress)
+
+	// Network writer
+	go func(userMessages chan *netwrk.LobbyMessage) {
+		for {
+			msg := <-userMessages
+			bytes, err := proto.Marshal(msg)
+			if err != nil {
+				log.Panic("Malformed proto message", err)
+			}
+			_, err = conn.Write(bytes)
+			if err == io.EOF {
+				log.Panic("Server disconnected sorry...")
+			} else if err != nil {
+				log.Panic("Error reading from server connection...")
+			}
+		}
+	}(egress)
+
+	// Network reader
+	go func(serverMessages chan *netwrk.LobbyMessage) {
+		buf := make([]byte, 1024)
+		for {
+			n, err := conn.Read(buf)
+			if err == io.EOF {
+				log.Panic("Server disconnected sorry...")
+			} else if err != nil {
+				log.Panic("Error reading from server connection...")
+			}
+
+			message := &netwrk.LobbyMessage{}
+
+			err = proto.Unmarshal(buf[:n], message)
+			if err != nil {
+				log.Panic("Error reading message from server")
+			}
+
+			serverMessages <- message
+
+		}
+	}(ingress)
+
+	_ = <-exit
 }
diff --git a/cmd/server/main.go b/cmd/server/main.go
index dbbcf8b..272af25 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -5,9 +5,12 @@ import (
 	"sshpong/internal/netwrk"
 )
 
+var exit chan bool
+
 func main() {
 	fmt.Println("Starting sshpong server!")
 
 	netwrk.Listen()
 
+	_ = <-exit
 }
diff --git a/internal/client/client_utils.go b/internal/client/client_utils.go
new file mode 100644
index 0000000..a05c920
--- /dev/null
+++ b/internal/client/client_utils.go
@@ -0,0 +1,71 @@
+package client
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"sshpong/internal/netwrk"
+	"strings"
+)
+
+func HandleUserInput(buf []byte) (*netwrk.LobbyMessage, error) {
+	input := string(buf)
+	args := strings.Fields(input)
+	switch args[0] {
+	case "invite":
+		if args[1] != "" {
+			return &netwrk.LobbyMessage{
+				Type:    "invite",
+				Content: args[1],
+			}, nil
+		} else {
+			fmt.Println("Please provide a player to invite ")
+		}
+	case "chat":
+		if args[1] != "" {
+			return &netwrk.LobbyMessage{
+				Type:    "chat",
+				Content: strings.Join(args[1:], " "),
+			}, nil
+		}
+	case "/":
+		if args[1] != "" {
+			return &netwrk.LobbyMessage{
+				Type:    "chat",
+				Content: strings.Join(args[1:], " "),
+			}, nil
+		}
+	case "quit":
+		return nil, io.EOF
+	case "q":
+		return nil, io.EOF
+	case "help":
+		return nil, fmt.Errorf("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
+	case "h":
+		return nil, fmt.Errorf("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
+	default:
+		return nil, fmt.Errorf("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
+	}
+	return nil, nil
+}
+
+func HandleServerMessage(message *netwrk.LobbyMessage) {
+	switch message.Type {
+	case "invite":
+		log.Println(message.PlayerId, "is inviting you to a game.", message.Content)
+	case "accepted":
+		log.Println(message.PlayerId, "accepted your invite.", message.Content)
+	case "text":
+		log.Println(message.PlayerId, ":", message.Content)
+	case "decline_game":
+		log.Println("Invite was declined:", message.Content)
+	case "disconnect":
+		log.Println("Got disconnect for player:", message.Content)
+	case "connect":
+		log.Println("Got connect for player:", message.Content)
+	case "pong":
+		log.Println("Received", message.Content)
+	default:
+		log.Println("Received", message.Content)
+	}
+}
diff --git a/internal/netwrk/client.go b/internal/netwrk/client.go
index be14b89..562110f 100644
--- a/internal/netwrk/client.go
+++ b/internal/netwrk/client.go
@@ -2,152 +2,26 @@ package netwrk
 
 import (
 	"fmt"
-	"log"
 	"net"
-	"strings"
 
 	"google.golang.org/protobuf/proto"
 )
 
-type LobbyPlayerStatus struct {
-	Username string
-	Status   string
-}
-
-type Interrupter struct {
-	MessageType string
-	Message     string
-	ReplyChan   chan string
-}
-
-var username string
-var lobby chan LobbyPlayerStatus
-var interruptChan chan Interrupter
-
-func ConnectToLobby(playerUsername string, messageOutputChan chan *LobbyMessage, lobbyMessageChan chan LobbyPlayerStatus, interruptChannel chan Interrupter) {
-	username = playerUsername
-	lobby = lobbyMessageChan
-	interruptChan = interruptChannel
-
+func ConnectToLobby(username string) (net.Conn, error) {
 	conn, err := net.Dial("tcp", "127.0.0.1:12345")
 	if err != nil {
-		fmt.Println("Sorry, failed to connect to server...")
-		return
+		return nil, fmt.Errorf("Sorry, failed to connect to server...")
 	}
 
 	loginMsg, err := proto.Marshal(&LobbyMessage{Type: "name", Content: username})
 	if err != nil {
-		fmt.Println("Sorry bro but your username is wack AF...")
+		return nil, fmt.Errorf("Sorry bro but your username is wack AF...")
 	}
 
 	_, err = conn.Write(loginMsg)
 	if err != nil {
-		fmt.Println("Sorry, could not communicate with server...")
+		return nil, fmt.Errorf("Sorry, could not communicate with server...")
 	}
 
-	fmt.Println("Starting client loop")
-	messageInputChan := make(chan *LobbyMessage)
-	go func() {
-		for {
-			messageBytes := make([]byte, 1024)
-
-			n, err := conn.Read(messageBytes)
-			if err != nil {
-				fmt.Println("Sorry, failed to read message from server...", err)
-			}
-
-			message := &LobbyMessage{}
-
-			err = proto.Unmarshal(messageBytes[:n], message)
-			if err != nil {
-				fmt.Println("Sorry, the server sent something weird back...")
-			}
-
-			messageInputChan <- message
-		}
-	}()
-	for {
-		select {
-		case msg := <-messageInputChan:
-			if isDone, gameID := handleLobbyMessage(conn, msg); isDone {
-				if gameID != "" {
-					interruptChan <- Interrupter{
-						MessageType: "game",
-						Message:     gameID,
-						ReplyChan:   make(chan string),
-					}
-					return
-				} else {
-					return
-				}
-			}
-		case msg := <-messageOutputChan:
-			fmt.Println("Sending message out", msg)
-			err := SendMessageToServer(conn, msg)
-			if err != nil {
-				fmt.Println("Error!", err)
-			}
-		}
-	}
-}
-
-func handleLobbyMessage(serverConn net.Conn, message *LobbyMessage) (bool, string) {
-	switch message.Type {
-	case "text":
-		fmt.Println(message.Content)
-		return false, ""
-	case "error":
-		fmt.Println("Error:", message.Content)
-		return false, ""
-	case "invite":
-		fmt.Println("GOT INVITE!")
-		replyChan := make(chan string)
-		interruptChan <- Interrupter{
-			MessageType: "invite",
-			Message:     fmt.Sprintf("Invite from player %s\nAccept: Y Decline: N", message.Content),
-			ReplyChan:   replyChan,
-		}
-		input := <-replyChan
-		if strings.ToLower(input) == "yes" || strings.ToLower(input) == "y" {
-			SendMessageToServer(serverConn, &LobbyMessage{Type: "accept_game", Content: username})
-			SendMessageToServer(serverConn, &LobbyMessage{Type: "quit", Content: username})
-			return true, message.Content
-		} else {
-			SendMessageToServer(serverConn, &LobbyMessage{Type: "decline_game", Content: username})
-		}
-		return false, ""
-	case "accept":
-		fmt.Println(message.Content, "accepted your invite. Game starting...")
-		SendMessageToServer(serverConn, &LobbyMessage{Type: "quit", Content: username})
-		return true, message.Content
-	case "decline_game":
-		fmt.Println("Sorry,", message.Content, "declined your game invite...")
-		return false, ""
-	case "connect":
-		lobby <- LobbyPlayerStatus{Username: message.Content, Status: "connected"}
-		fmt.Println(message.Content, "connected!")
-		return false, ""
-	case "disconnect":
-		lobby <- LobbyPlayerStatus{Username: message.Content, Status: "disconnected"}
-		fmt.Println(message.Content, "disconnected!")
-		return false, ""
-	case "pong":
-		log.Println("PoNg!")
-		return false, ""
-	default:
-		log.Println("Got message", message)
-	}
-	return false, ""
-}
-
-func SendMessageToServer(connection net.Conn, message *LobbyMessage) error {
-	bytes, err := proto.Marshal(message)
-	if err != nil {
-		return fmt.Errorf("Error marshalling message. Your protobuf is wack yo.")
-	}
-	_, err = connection.Write(bytes)
-	if err != nil {
-		return fmt.Errorf("Error writing to client connection")
-	}
-	return nil
+	return conn, nil
 }
diff --git a/internal/netwrk/lobby.go b/internal/netwrk/lobby.go
index e2d610d..938caa3 100644
--- a/internal/netwrk/lobby.go
+++ b/internal/netwrk/lobby.go
@@ -1,7 +1,6 @@
 package netwrk
 
 import (
-	"fmt"
 	"io"
 	"log"
 	"net"
@@ -14,15 +13,16 @@ func handleLobbyConnection(conn net.Conn) {
 
 	messageBytes := make([]byte, 4096)
 
-	recvMessageChan := make(chan *LobbyMessage)
+	ingress := make(chan *LobbyMessage)
+	egress := make(chan *LobbyMessage)
+
+	// Network Reader
 	go func() {
 		for {
-			fmt.Println("READING!")
 			n, err := conn.Read(messageBytes)
 			if err == io.EOF {
 				return
 			}
-			fmt.Println("READ something!")
 			if err != nil {
 				log.Printf("Error reading message %v", err)
 				return
@@ -32,106 +32,103 @@ func handleLobbyConnection(conn net.Conn) {
 
 			err = proto.Unmarshal(messageBytes[:n], &message)
 			if err != nil {
-				log.Println("Invalid message received from client")
+				log.Println("Invalid message received from client", err)
 			}
-			recvMessageChan <- &message
+			ingress <- &message
 		}
 	}()
 
-	for {
-
-		select {
-		case msg := <-recvMessageChan:
-			if isDone, err := handleClientLobbyMessage(conn, msg); err != nil || isDone {
-				log.Println(err)
-				return
+	// Network Writer
+	go func() {
+		for {
+			msg := <-egress
+			bytes, err := proto.Marshal(msg)
+			if err != nil {
+				log.Println("Error marshalling message to send to user...", err)
+			}
+			_, err = conn.Write(bytes)
+			if err == io.EOF {
+				log.Println("User has disconnected", err)
+				ingress <- &LobbyMessage{Type: "disconnect"}
+			}
+			if err != nil {
+				log.Println("Error writing to user...", err)
 			}
-			fmt.Println("Handled message")
 		}
-	}
+	}()
+
+	// Client message handler
+	go func() {
+		for {
+			msg := <-ingress
+			serverMsg, err := handleClientLobbyMessage(msg)
+			if err != nil {
+				log.Println("Error handling client lobby message...", err)
+			}
+			if serverMsg != nil {
+				egress <- serverMsg
+			}
+		}
+	}()
 }
 
 // Returns a bool of whether the player has disconnected from the lobby and an error
-func handleClientLobbyMessage(playerConnection net.Conn, message *LobbyMessage) (bool, error) {
+func handleClientLobbyMessage(message *LobbyMessage) (*LobbyMessage, error) {
 	switch message.Type {
 	case "name":
-		_, ok := clientPool.clients[message.Content]
+		_, ok := lobbyMembers.Load(message.Content)
 		if ok {
-			SendMessageToClient(playerConnection, &LobbyMessage{Type: "error", Content: "Sorry, that name is already taken"})
-			return false, nil
-		}
-		playerID := message.Content
-		clientPool.clients[playerID] = Client{
-			name:  playerID,
-			conn:  playerConnection,
-			ready: false,
-		}
-		for _, player := range clientPool.clients {
-			err := SendMessageToClient(playerConnection, &LobbyMessage{PlayerId: player.name, Type: "connect", Content: player.name})
-			if err != nil {
-				log.Println("There was an error sending the list of lobby players to client", message.Content)
-			}
+			return &LobbyMessage{Type: "name_error", Content: "Sorry, that name is already taken, please try a different name"}, nil
 		}
+		username := message.Content
+
+		// Send all client messages
+		lobbyMembers.Range(func(lobbyUsername string, client Client) bool {
+			externalMessageChan <- ExternalMessage{Target: username, Message: &LobbyMessage{Type: "connect", Content: lobbyUsername}}
+			return true
+		})
 
 		log.Println("Broadcasting new player", message.Content)
 
-		broadcastToLobby(&LobbyMessage{PlayerId: "", Type: "connect", Content: playerID})
+		broadcastToLobby(&LobbyMessage{PlayerId: "", Type: "connect", Content: username})
 
-		return false, SendMessageToClient(playerConnection, &LobbyMessage{PlayerId: playerID, Type: "name", Content: playerID})
+		return &LobbyMessage{PlayerId: username, Type: "name", Content: username}, nil
 	case "invite":
 		log.Println("Got invite for player:", message.Content)
-		invitee, ok := clientPool.clients[message.Content]
+		invitee, ok := lobbyMembers[message.Content]
 		if !ok {
-			SendMessageToClient(playerConnection, &LobbyMessage{Type: "text", Content: "Sorry, that player is not available..."})
-			return false, nil
+			return &LobbyMessage{Type: "text", Content: "Sorry, that player is not available..."}, nil
 		}
-		SendMessageToClient(invitee.conn, &LobbyMessage{Type: "invite", Content: message.PlayerId})
-		return false, nil
+		return &LobbyMessage{Type: "invite", Content: message.PlayerId}, nil
 	case "accept_game":
-		player := clientPool.clients[message.Content]
+		player := lobbyMembers[message.Content]
 
-		if err := SendMessageToClient(player.conn, &LobbyMessage{Type: "accept", Content: ""}); err != nil {
-			SendMessageToClient(playerConnection, &LobbyMessage{Type: "error", Content: "Sorry that game is no longer available..."})
-			return false, nil
-		}
+		return &LobbyMessage{Type: "accept", Content: ""}, nil
 
-		return true, nil
 	case "chat":
 		broadcastToLobby(&LobbyMessage{PlayerId: message.PlayerId, Type: "text", Content: message.Content})
-		return false, nil
+		return nil, nil
 	case "decline_game":
-		inviter := clientPool.clients[message.Content]
-		SendMessageToClient(inviter.conn, &LobbyMessage{Type: "decline_game", Content: message.PlayerId})
-		return false, nil
+		inviter := lobbyMembers[message.Content]
+		return &LobbyMessage{Type: "decline_game", Content: message.PlayerId}, nil
 	case "quit":
-		delete(clientPool.clients, message.PlayerId)
+		delete(lobbyMembers, message.PlayerId)
 		broadcastToLobby(&LobbyMessage{Type: "disconnect", Content: message.PlayerId})
-		return true, nil
+		return nil, nil
 	case "ping":
-		SendMessageToClient(playerConnection, &LobbyMessage{Type: "pong", Content: "pong"})
-		return false, nil
+		return &LobbyMessage{Type: "pong", Content: "pong"}, nil
 	default:
-		SendMessageToClient(playerConnection, &LobbyMessage{Type: "pong", Content: "pong"})
-		return false, nil
+		return &LobbyMessage{Type: "pong", Content: "pong"}, nil
 	}
 }
 
-func SendMessageToClient(connection net.Conn, message *LobbyMessage) error {
-	bytes, err := proto.Marshal(message)
-	if err != nil {
-		return fmt.Errorf("Error marshalling message. Your protobuf is wack yo.")
-	}
-	_, err = connection.Write(bytes)
-	if err != nil {
-		return fmt.Errorf("Error writing to client connection")
-	}
-	fmt.Println("Sent message to client")
-	return nil
-}
-
 func broadcastToLobby(message *LobbyMessage) {
-	for _, player := range clientPool.clients {
-		err := SendMessageToClient(player.conn, message)
+	for _, player := range lobbyMembers {
+		bytes, err := proto.Marshal(message)
+		if err != nil {
+			log.Println("Error marshalling broadcast message", err)
+		}
+		_, err = player.Conn.Write(bytes)
 		if err != nil {
 			log.Println("Error broadcasting to clients...", err)
 		}
diff --git a/internal/netwrk/netwrk.go b/internal/netwrk/netwrk.go
index 7e5a5c9..468ae7c 100644
--- a/internal/netwrk/netwrk.go
+++ b/internal/netwrk/netwrk.go
@@ -3,36 +3,40 @@ package netwrk
 import (
 	"log"
 	"net"
+	sync "sync"
 )
 
 type Client struct {
-	name  string
-	conn  net.Conn
-	ready bool
+	Username string
+	Conn     net.Conn
 }
 
-type ClientPool struct {
-	clients map[string]Client
+type LobbyPlayersMessage struct {
+	Type        string
+	Username    string
+	IsAvailable chan bool
 }
 
-type GameClients struct {
-	client1 chan GameMessage
-	client2 chan GameMessage
+type ExternalMessage struct {
+	Target  string
+	Message *LobbyMessage
 }
 
-type GameChans struct {
-	games map[string]GameClients
-}
+var lobbyListener chan LobbyPlayersMessage
+var externalMessageChan chan ExternalMessage
 
-var clientPool *ClientPool
-var gameChans *GameChans
+var lobbyMembers sync.Map
+
+func init() {
+	lobbyListener = make(chan LobbyPlayersMessage)
+	externalMessageChan = make(chan ExternalMessage)
+
+	lobbyMembers = sync.Map{}
+}
 
 // Starts listening on port 12345 for TCP connections
 // Also creates client pool and game connection singletons
-func Listen() {
-	clientPool = &ClientPool{
-		clients: map[string]Client{},
-	}
+func LobbyListen() {
 
 	listener, err := net.Listen("tcp", "127.0.0.1:12345")
 	if err != nil {
@@ -41,21 +45,18 @@ func Listen() {
 
 	defer listener.Close()
 
-	go func() {
-		for {
-			conn, err := listener.Accept()
-			log.Println("got a connection!")
-			if err != nil {
-				log.Println(err)
-				continue
-			}
-			go handleLobbyConnection(conn)
+	for {
+		conn, err := listener.Accept()
+		log.Println("got a connection!")
+		if err != nil {
+			log.Println(err)
+			continue
 		}
-	}()
-
-	gameChans = &GameChans{
-		games: map[string]GameClients{},
+		go handleLobbyConnection(conn)
 	}
+}
+
+func GamesListen() {
 
 	gameListener, err := net.Listen("tcp", "127.0.0.1:42069")
 	if err != nil {