diff --git a/api/request_processor.go b/api/request_processor.go index 9ec116b..7b6e320 100644 --- a/api/request_processor.go +++ b/api/request_processor.go @@ -333,6 +333,11 @@ sessionLoop: rp.sessionManager.Communicate(sessionId, receiverSessionId, msg, mc.MessageTypeJSON) break sessionLoop + case mc.CodePlayerInteraction: + if err := rp.sessionManager.Communicate(sessionId, receiverSessionId, payload, mc.MessageTypeBytes); err != nil { + break sessionLoop + } + default: respInvalidSignal := mc.NewMessage[mc.NoPayload](mc.CodeInvalidSignal) respInvalidSignal.AddError("", "invalid code in the incoming payload") diff --git a/cmd/main.go b/cmd/main.go index 0f767df..4341633 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,9 +1,13 @@ package main import ( + "context" "log" "net/http" "os" + "os/signal" + "syscall" + "time" "github.com/joho/godotenv" "github.com/saeidalz13/battleship-backend/api" @@ -42,6 +46,23 @@ func main() { mux := http.NewServeMux() mux.Handle("GET /battleship", requestProcessor) - log.Printf("Listening to port %s\n", port) - log.Fatalln(http.ListenAndServe("0.0.0.0:"+port, mux)) + s := &http.Server{ + Addr: ":" + port, + Handler: mux, + } + + go func() { + log.Printf("Listening to port %s\n", port) + log.Fatalln(s.ListenAndServe()) + }() + + sigChan := make(chan os.Signal, 1) + + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + sig := <-sigChan + log.Println("Server termination signal from OS, graceful shutdown\treason:", sig) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + s.Shutdown(ctx) } diff --git a/models/connection/message.go b/models/connection/message.go index 8e279a2..42d5f7e 100644 --- a/models/connection/message.go +++ b/models/connection/message.go @@ -18,3 +18,7 @@ func (m *Message[T]) AddPayload(payload T) { func (m *Message[T]) AddError(errorDetails, message string) { m.Error = NewRespErr(errorDetails, message) } + +type PlayerInteraction struct { + Content string `json:"content"` +} diff --git a/models/connection/signal.go b/models/connection/signal.go index 1f9f6c4..0f65a90 100644 --- a/models/connection/signal.go +++ b/models/connection/signal.go @@ -28,6 +28,9 @@ const ( CodeRematchCallAccepted CodeRematchCallRejected CodeRematch + + // Players can send template texts and emojis to each other + CodePlayerInteraction ) type Signal struct { diff --git a/test/ws_test.go b/test/ws_test.go index 619284d..e9707ef 100644 --- a/test/ws_test.go +++ b/test/ws_test.go @@ -281,6 +281,52 @@ func TestReadyGame(t *testing.T) { } } +func TestPlayerInteraction(t *testing.T) { + msg := mc.NewMessage[mc.PlayerInteraction](mc.CodePlayerInteraction) + msg.AddPayload(mc.PlayerInteraction{Content: "salam!"}) + + tests := []Test[mc.Message[mc.PlayerInteraction], mc.Message[mc.PlayerInteraction]]{ + { + name: "successful msg host to join", + expectedCode: mc.CodePlayerInteraction, + reqPayload: msg, + respPayload: mc.Message[mc.PlayerInteraction]{}, + conn: HostConn, + otherConn: JoinConn, + }, + { + name: "successful msg join to host", + expectedCode: mc.CodePlayerInteraction, + reqPayload: msg, + respPayload: mc.Message[mc.PlayerInteraction]{}, + conn: JoinConn, + otherConn: HostConn, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Client writes to its own connection + if err := test.conn.WriteJSON(test.reqPayload); err != nil { + t.Fatal(err) + } + + // Server writes it to ther other connection + if err := test.otherConn.ReadJSON(&test.respPayload); err != nil { + t.Fatal(err) + } + + if test.respPayload.Code != test.expectedCode { + t.Fatalf("expected status: %d\t got: %d", test.expectedCode, test.respPayload.Code) + } + + if !reflect.DeepEqual(test.reqPayload, test.respPayload) { + t.Fatalf("expected resp payload: %+v\n got: %+v", test.expectedRespPayload, test.respPayload) + } + }) + } +} + func TestAttack(t *testing.T) { tests := []Test[mc.Message[mc.ReqAttack], mc.Message[mc.RespAttack]]{ {