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 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 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 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 + +