From 35d8ebc4594384009472588a6034d260a36d56de Mon Sep 17 00:00:00 2001
From: Beric Bearnson <37596980+bericyb@users.noreply.github.com>
Date: Wed, 14 Aug 2024 23:33:05 -0600
Subject: [PATCH] Broken networking

---
 cmd/client/main.go                |  95 +++++++++++++++++++
 cmd/server/main.go                |   1 +
 internal/netwrk/client.go         | 153 ++++++++++++++++++++++++++++++
 internal/netwrk/lobby.go          |  68 +++++++++----
 internal/netwrk/lobby_handlers.go |   2 -
 internal/netwrk/netwrk.go         |  21 ++--
 todo.txt                          |  11 +++
 7 files changed, 317 insertions(+), 34 deletions(-)
 create mode 100644 cmd/client/main.go
 create mode 100644 internal/netwrk/client.go
 delete mode 100644 internal/netwrk/lobby_handlers.go
 create mode 100644 todo.txt

diff --git a/cmd/client/main.go b/cmd/client/main.go
new file mode 100644
index 0000000..11b1bda
--- /dev/null
+++ b/cmd/client/main.go
@@ -0,0 +1,95 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"sshpong/internal/netwrk"
+	"strings"
+)
+
+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)
+
+	buf := make([]byte, 1024)
+
+	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")
+			}
+
+		}
+
+	}
+
+}
diff --git a/cmd/server/main.go b/cmd/server/main.go
index ec66ff0..dbbcf8b 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -9,4 +9,5 @@ func main() {
 	fmt.Println("Starting sshpong server!")
 
 	netwrk.Listen()
+
 }
diff --git a/internal/netwrk/client.go b/internal/netwrk/client.go
new file mode 100644
index 0000000..be14b89
--- /dev/null
+++ b/internal/netwrk/client.go
@@ -0,0 +1,153 @@
+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
+
+	conn, err := net.Dial("tcp", "127.0.0.1:12345")
+	if err != nil {
+		fmt.Println("Sorry, failed to connect to server...")
+		return
+	}
+
+	loginMsg, err := proto.Marshal(&LobbyMessage{Type: "name", Content: username})
+	if err != nil {
+		fmt.Println("Sorry bro but your username is wack AF...")
+	}
+
+	_, err = conn.Write(loginMsg)
+	if err != nil {
+		fmt.Println("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
+}
diff --git a/internal/netwrk/lobby.go b/internal/netwrk/lobby.go
index c2b211d..e2d610d 100644
--- a/internal/netwrk/lobby.go
+++ b/internal/netwrk/lobby.go
@@ -2,9 +2,9 @@ package netwrk
 
 import (
 	"fmt"
+	"io"
 	"log"
 	"net"
-	"time"
 
 	"google.golang.org/protobuf/proto"
 )
@@ -14,29 +14,45 @@ func handleLobbyConnection(conn net.Conn) {
 
 	messageBytes := make([]byte, 4096)
 
-	for {
-		n, err := conn.Read(messageBytes)
-		if err != nil {
-			log.Printf("Error reading message %v", err)
-			return
-		}
+	recvMessageChan := make(chan *LobbyMessage)
+	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
+			}
 
-		if isDone, err := handleLobbyMessage(conn, messageBytes[:n]); err != nil || isDone {
-			return
+			message := LobbyMessage{}
+
+			err = proto.Unmarshal(messageBytes[:n], &message)
+			if err != nil {
+				log.Println("Invalid message received from client")
+			}
+			recvMessageChan <- &message
+		}
+	}()
+
+	for {
+
+		select {
+		case msg := <-recvMessageChan:
+			if isDone, err := handleClientLobbyMessage(conn, msg); err != nil || isDone {
+				log.Println(err)
+				return
+			}
+			fmt.Println("Handled message")
 		}
 	}
 }
 
 // Returns a bool of whether the player has disconnected from the lobby and an error
-func handleLobbyMessage(playerConnection net.Conn, bytes []byte) (bool, error) {
-
-	message := LobbyMessage{}
-
-	err := proto.Unmarshal(bytes, &message)
-	if err != nil {
-		return false, fmt.Errorf("Invalid message received from client")
-	}
-
+func handleClientLobbyMessage(playerConnection net.Conn, message *LobbyMessage) (bool, error) {
 	switch message.Type {
 	case "name":
 		_, ok := clientPool.clients[message.Content]
@@ -57,10 +73,13 @@ func handleLobbyMessage(playerConnection net.Conn, bytes []byte) (bool, error) {
 			}
 		}
 
+		log.Println("Broadcasting new player", message.Content)
+
 		broadcastToLobby(&LobbyMessage{PlayerId: "", Type: "connect", Content: playerID})
 
 		return false, SendMessageToClient(playerConnection, &LobbyMessage{PlayerId: playerID, Type: "name", Content: playerID})
-	case "invite_player":
+	case "invite":
+		log.Println("Got invite for player:", message.Content)
 		invitee, ok := clientPool.clients[message.Content]
 		if !ok {
 			SendMessageToClient(playerConnection, &LobbyMessage{Type: "text", Content: "Sorry, that player is not available..."})
@@ -77,9 +96,12 @@ func handleLobbyMessage(playerConnection net.Conn, bytes []byte) (bool, error) {
 		}
 
 		return true, nil
+	case "chat":
+		broadcastToLobby(&LobbyMessage{PlayerId: message.PlayerId, Type: "text", Content: message.Content})
+		return false, nil
 	case "decline_game":
 		inviter := clientPool.clients[message.Content]
-		SendMessageToClient(inviter.conn, &LobbyMessage{Type: "decline_game", Content: ""})
+		SendMessageToClient(inviter.conn, &LobbyMessage{Type: "decline_game", Content: message.PlayerId})
 		return false, nil
 	case "quit":
 		delete(clientPool.clients, message.PlayerId)
@@ -103,11 +125,15 @@ func SendMessageToClient(connection net.Conn, message *LobbyMessage) error {
 	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 {
-		SendMessageToClient(player.conn, message)
+		err := SendMessageToClient(player.conn, message)
+		if err != nil {
+			log.Println("Error broadcasting to clients...", err)
+		}
 	}
 }
diff --git a/internal/netwrk/lobby_handlers.go b/internal/netwrk/lobby_handlers.go
deleted file mode 100644
index 139597f..0000000
--- a/internal/netwrk/lobby_handlers.go
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/internal/netwrk/netwrk.go b/internal/netwrk/netwrk.go
index 3563347..7e5a5c9 100644
--- a/internal/netwrk/netwrk.go
+++ b/internal/netwrk/netwrk.go
@@ -34,7 +34,7 @@ func Listen() {
 		clients: map[string]Client{},
 	}
 
-	listener, err := net.Listen("tcp", ":12345")
+	listener, err := net.Listen("tcp", "127.0.0.1:12345")
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -44,6 +44,7 @@ func Listen() {
 	go func() {
 		for {
 			conn, err := listener.Accept()
+			log.Println("got a connection!")
 			if err != nil {
 				log.Println(err)
 				continue
@@ -56,20 +57,18 @@ func Listen() {
 		games: map[string]GameClients{},
 	}
 
-	gameListener, err := net.Listen("tcp", ":42069")
+	gameListener, err := net.Listen("tcp", "127.0.0.1:42069")
 	if err != nil {
 		log.Fatal(err)
 	}
 
 	defer gameListener.Close()
-	go func() {
-		for {
-			conn, err := gameListener.Accept()
-			if err != nil {
-				log.Println(err)
-				continue
-			}
-			handleGameConnection(conn)
+	for {
+		conn, err := gameListener.Accept()
+		if err != nil {
+			log.Println(err)
+			continue
 		}
-	}()
+		handleGameConnection(conn)
+	}
 }
diff --git a/todo.txt b/todo.txt
new file mode 100644
index 0000000..0937d70
--- /dev/null
+++ b/todo.txt
@@ -0,0 +1,11 @@
+- Get a lobby going
+	- Join lobby
+	- See other players in lobby
+	- Leave lobby and update 
+	- Send invite and accept
+- Get a game going
+	- Both players in game
+	- Send messsage back and forth
+	- End game and go back to lobby
+
+