From 7a44092b2ff88ba8683021ef53a07cf8f43f1763 Mon Sep 17 00:00:00 2001 From: Beric Bearnson Date: Wed, 14 Aug 2024 17:26:51 -0600 Subject: [PATCH] more networking --- internal/netwrk/game.go | 45 ++++++ internal/netwrk/game_messages.pb.go | 2 +- internal/netwrk/lobby.go | 113 +++++++++++++++ internal/netwrk/lobby_messages.pb.go | 28 ++-- internal/netwrk/netwrk.go | 203 +-------------------------- proto/lobby_messages.proto | 5 +- 6 files changed, 188 insertions(+), 208 deletions(-) create mode 100644 internal/netwrk/game.go create mode 100644 internal/netwrk/lobby.go diff --git a/internal/netwrk/game.go b/internal/netwrk/game.go new file mode 100644 index 0000000..9e3ec39 --- /dev/null +++ b/internal/netwrk/game.go @@ -0,0 +1,45 @@ +package netwrk + +import ( + "log" + "net" +) + +func handleGameConnection(conn net.Conn) { + defer conn.Close() + + messageBytes := make([]byte, 126) + + n, err := conn.Read(messageBytes) + if err != nil { + log.Printf("Error reading game ID on connection", err) + } + + gameID := string(messageBytes[:n]) + if err != nil { + log.Printf("Game id was not a string?", err) + } + + clientChan := make(chan GameMessage) + + n, err = conn.Read(messageBytes) + if err != nil { + log.Printf("Error reading message %v", err) + return + } + + gameClients, ok := gameChans.games[gameID] + if !ok { + newGameClients := GameClients{ + client1: clientChan, + client2: nil, + } + gameChans.games[gameID] = newGameClients + } else { + gameClients.client2 = clientChan + } +} + +func handleGameMessage(conn net.Conn, message *GameMessage) error { + return nil +} diff --git a/internal/netwrk/game_messages.pb.go b/internal/netwrk/game_messages.pb.go index 1142d2b..633a1a8 100644 --- a/internal/netwrk/game_messages.pb.go +++ b/internal/netwrk/game_messages.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.3 +// protoc v5.27.1 // source: proto/game_messages.proto package netwrk diff --git a/internal/netwrk/lobby.go b/internal/netwrk/lobby.go new file mode 100644 index 0000000..c2b211d --- /dev/null +++ b/internal/netwrk/lobby.go @@ -0,0 +1,113 @@ +package netwrk + +import ( + "fmt" + "log" + "net" + "time" + + "google.golang.org/protobuf/proto" +) + +func handleLobbyConnection(conn net.Conn) { + defer conn.Close() + + messageBytes := make([]byte, 4096) + + for { + n, err := conn.Read(messageBytes) + if err != nil { + log.Printf("Error reading message %v", err) + return + } + + if isDone, err := handleLobbyMessage(conn, messageBytes[:n]); err != nil || isDone { + return + } + } +} + +// 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") + } + + switch message.Type { + case "name": + _, ok := clientPool.clients[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) + } + } + + broadcastToLobby(&LobbyMessage{PlayerId: "", Type: "connect", Content: playerID}) + + return false, SendMessageToClient(playerConnection, &LobbyMessage{PlayerId: playerID, Type: "name", Content: playerID}) + case "invite_player": + invitee, ok := clientPool.clients[message.Content] + if !ok { + SendMessageToClient(playerConnection, &LobbyMessage{Type: "text", Content: "Sorry, that player is not available..."}) + return false, nil + } + SendMessageToClient(invitee.conn, &LobbyMessage{Type: "invite", Content: message.PlayerId}) + return false, nil + case "accept_game": + player := clientPool.clients[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 true, nil + case "decline_game": + inviter := clientPool.clients[message.Content] + SendMessageToClient(inviter.conn, &LobbyMessage{Type: "decline_game", Content: ""}) + return false, nil + case "quit": + delete(clientPool.clients, message.PlayerId) + broadcastToLobby(&LobbyMessage{Type: "disconnect", Content: message.PlayerId}) + return true, nil + case "ping": + SendMessageToClient(playerConnection, &LobbyMessage{Type: "pong", Content: "pong"}) + return false, nil + default: + SendMessageToClient(playerConnection, &LobbyMessage{Type: "pong", Content: "pong"}) + return false, 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") + } + return nil +} + +func broadcastToLobby(message *LobbyMessage) { + for _, player := range clientPool.clients { + SendMessageToClient(player.conn, message) + } +} diff --git a/internal/netwrk/lobby_messages.pb.go b/internal/netwrk/lobby_messages.pb.go index 5ca1a59..2010eed 100644 --- a/internal/netwrk/lobby_messages.pb.go +++ b/internal/netwrk/lobby_messages.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.3 +// protoc v5.27.1 // source: proto/lobby_messages.proto package netwrk @@ -25,8 +25,9 @@ type LobbyMessage struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + PlayerId string `protobuf:"bytes,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` } func (x *LobbyMessage) Reset() { @@ -61,6 +62,13 @@ func (*LobbyMessage) Descriptor() ([]byte, []int) { return file_proto_lobby_messages_proto_rawDescGZIP(), []int{0} } +func (x *LobbyMessage) GetPlayerId() string { + if x != nil { + return x.PlayerId + } + return "" +} + func (x *LobbyMessage) GetType() string { if x != nil { return x.Type @@ -80,12 +88,14 @@ var File_proto_lobby_messages_proto protoreflect.FileDescriptor var file_proto_lobby_messages_proto_rawDesc = []byte{ 0x0a, 0x1a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x6e, 0x65, - 0x74, 0x77, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x22, 0x3c, 0x0a, 0x0c, 0x4c, 0x6f, 0x62, 0x62, 0x79, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x72, - 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x77, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x22, 0x59, 0x0a, 0x0c, 0x4c, 0x6f, 0x62, 0x62, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x72, 0x6b, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/netwrk/netwrk.go b/internal/netwrk/netwrk.go index d685a33..3563347 100644 --- a/internal/netwrk/netwrk.go +++ b/internal/netwrk/netwrk.go @@ -1,13 +1,8 @@ package netwrk import ( - "fmt" "log" "net" - "time" - - "github.com/google/uuid" - "google.golang.org/protobuf/proto" ) type Client struct { @@ -61,9 +56,15 @@ func Listen() { games: map[string]GameClients{}, } + gameListener, err := net.Listen("tcp", ":42069") + if err != nil { + log.Fatal(err) + } + + defer gameListener.Close() go func() { for { - conn, err := listener.Accept() + conn, err := gameListener.Accept() if err != nil { log.Println(err) continue @@ -72,193 +73,3 @@ func Listen() { } }() } - -func handleGameConnection(conn net.Conn) { - defer conn.Close() - - messageBytes := make([]byte, 126) - - n, err := conn.Read(messageBytes) - if err != nil { - log.Printf("Error reading game ID on connection", err) - } - - gameID := string(messageBytes[:n]) - if err != nil { - log.Printf("Game id was not a string?", err) - } - - clientChan := make(chan GameMessage) - - n, err := conn.Read(messageBytes) - if err != nil { - log.Printf("Error reading message %v", err) - return - } - - gameID := messageBytes[:n] - - clientChan, ok := gameChans[gameID] - if !ok { - GameClients{ - client1: clientChan, - client2: nil, - } - } -} - -func handleGameMessage(conn net.Conn, message GameMessage) error { - return nil -} - -func handleLobbyConnection(conn net.Conn) { - defer conn.Close() - - messageBytes := make([]byte, 4096) - - for { - n, err := conn.Read(messageBytes) - if err != nil { - log.Printf("Error reading message %v", err) - return - } - - if isDone, err := handleLobbyMessage(conn, messageBytes[:n]); err != nil || isDone { - return - } - } -} - -// 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") - } - - switch message.Type { - case "name": - clientPool.clients[uuid.New().String()] = Client{ - name: message.Content, - conn: playerConnection, - ready: false, - } - break - case "invite_player": - invitee, ok := clientPool.clients[message.Content] - if !ok { - SendMessageToClient(playerConnection, &LobbyMessage{Type: "text", Content: "Sorry that player is not available..."}) - return false, nil - } - SendMessageToClient(invitee.conn, &LobbyMessage{Type: "invite", Content: playerID}) - return false, nil - case "cancel_invite": - - case "accept_game": - AcceptGame(message.Content) - return true, nil - case "decline_game": - DeclineGame() - return false, nil - case "quit": - DeletePlayer(message.Content) - return true, nil - case "ping": - PongPlayer(message.Content) - return false, nil - default: - PongPlayer(message.Content) - return false, nil - } - return false, 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") - } - return nil -} - -func GetPool() (map[string]Client, bool) { - if clientPool.clients != nil { - return clientPool.clients, true - } - return clientPool.clients, false -} - -func CreateGame(clientID1, clientID2 string) (string, error) { - client1, ok := clientPool.clients[clientID1] - if !ok { - return "", fmt.Errorf("Client 1 was not found in client pool :(") - } - if err := client1.conn.SetWriteDeadline(time.Time{}); err != nil { - return "", fmt.Errorf("Client 1 was not responsive") - } - - client2, ok := clientPool.clients[clientID2] - if !ok { - return "", fmt.Errorf("Client 2 was not found in client pool :(") - } - if err := client2.conn.SetWriteDeadline(time.Time{}); err != nil { - return "", fmt.Errorf("Client 2 was not responsive") - } - - gameID := uuid.New().String() - gameConnections.games[gameID] = GameClients{ - client1: client1, - client2: client2, - } - - return gameID, nil -} - -func SendGameUpdateToLobbyClients(gameID string, message *LobbyMessage) error { - clients, ok := gameConnections.games[gameID] - if !ok { - return fmt.Errorf("Could not find game clients record") - } - - bytes, err := proto.Marshal(message) - if err != nil { - return fmt.Errorf("message could not be marshalled") - } - - _, err = clients.client1.conn.Write(bytes) - if err != nil { - return fmt.Errorf("Could not write to client1 connection") - } - _, err = clients.client1.conn.Write(bytes) - if err != nil { - return fmt.Errorf("Could not write to client2 connection") - } - - return nil -} - -func PingAndCleanLobbyClients() { - ping := []byte("ping") - - deadClients := []string{} - for id, client := range clientPool.clients { - client.conn.SetWriteDeadline(time.Now().Add(time.Second)) - _, err := client.conn.Write(ping) - if err != nil { - log.Println("Could not write to client, deleting connection:", id) - deadClients = append(deadClients, id) - } - } - - for _, id := range deadClients { - delete(clientPool.clients, id) - } -} diff --git a/proto/lobby_messages.proto b/proto/lobby_messages.proto index bfd4797..76d45c9 100644 --- a/proto/lobby_messages.proto +++ b/proto/lobby_messages.proto @@ -4,6 +4,7 @@ package netwrk.v1; option go_package = "./netwrk"; message LobbyMessage { - string type = 1; - string content = 2; + string player_id = 1; + string type = 2; + string content = 3; }