Explorar o código

Synced multiplayer

Aaro Saila hai 3 semanas
pai
achega
2ffa0bdeb7

+ 2 - 1
meson.build

@@ -16,7 +16,8 @@ c_args = [
 common_src = [
   'src/common/GameState_net.c',
   'src/common/game.c',
-  'src/common/networking.c'
+  'src/common/networking.c',
+  'src/common/util.c'
 ]
 
 common_include = include_directories(

+ 6 - 1
src/common/GameState_net.c

@@ -11,7 +11,7 @@ static void MovingObject_to_MovingObjectMsg(const MovingObject *const src, Movin
     dst->speed = src->speed;
 }
 
-void GameState_to_GameStateMsg(const GameState *const src, GameStateMsg *const dst) {
+void GameState_to_GameStateMsg(const GameState *const src, GameStateMsg *const dst, const bool p1) {
     dst->ai_enabled = (uint8_t) src->ai_enabled;
     MovingObject_to_MovingObjectMsg(&src->paddle_l, &dst->paddle_l);
     MovingObject_to_MovingObjectMsg(&src->paddle_r, &dst->paddle_r);
@@ -22,6 +22,11 @@ void GameState_to_GameStateMsg(const GameState *const src, GameStateMsg *const d
     dst->ball_start_dir_y = src->ball_start_dir[1];
     dst->score_l = src->score_l;
     dst->score_r = src->score_r;
+
+    if (!p1) {
+        dst->ball.pos_x = 1.0f - dst->ball.pos_x;
+        dst->ball.dirv_x *= -1.0f;
+    }
 }
 
 static void MovingObjectMsg_to_MovingObject(const MovingObjectMsg *const src, MovingObject *const dst) {

+ 1 - 1
src/common/GameState_net.h

@@ -27,5 +27,5 @@ typedef struct [[gnu::packed]] {
     uint8_t score_r;
 } GameStateMsg;
 
-void GameState_to_GameStateMsg(const GameState *const src, GameStateMsg *const dst);
+void GameState_to_GameStateMsg(const GameState *const src, GameStateMsg *const dst, const bool p1);
 void GameStateMsg_to_GameState(const GameStateMsg *const src, GameState *const dst);

+ 49 - 16
src/common/game.c

@@ -27,6 +27,18 @@ const GameStateParameters game_default_params = {
     .ball_speed = 0.7f,
 };
 
+const char *GameStateEvent_to_str(const GameStateEvent evt) {
+#define X(evt) \
+    case evt: \
+        return #evt;
+
+    switch (evt) {
+        XGameStateEvent(X)
+    }
+
+#undef X
+}
+
 GameState GameState_new(
     const GameStateParameters params,
     const bool ai_enabled
@@ -104,35 +116,33 @@ static void game_next_round(GameState *const state) {
     glmc_vec2_copy(state->ball_start_dir, state->ball.dirv);
 }
 
-static bool ball_bounds_collision_check(GameState *const state, vec2 new_pos) {
+static GameStateEvent ball_bounds_collision_check(GameState *const state, vec2 new_pos) {
     MovingObject *const ball = &state->ball;
 
     const float wd2 = ball->w / 2.0f;
     const float hd2 = ball->h / 2.0f;
 
     if (new_pos[0] + wd2 <= 0.0f) {
-        state->score_r++;
-        game_next_round(state);
-        return true;
+        return GSEVENT_R_SCORE;
     } else if (new_pos[0] - wd2 >= 1.0f) {
-        state->score_l++;
-        game_next_round(state);
-        return true;
+        return GSEVENT_L_SCORE;
     }
 
     if (new_pos[1] - hd2 < 0.0f) {
         new_pos[1] = hd2;
         glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, -1.0f }, ball->dirv);
+        return GSEVENT_BALL_DIR_CHANGE;
     }
     if (new_pos[1] + hd2 > 1.0f) {
         new_pos[1] = 1.0f - hd2;
         glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, 1.0f }, ball->dirv);
+        return GSEVENT_BALL_DIR_CHANGE;
     }
 
-    return false;
+    return GSEVENT_NONE;
 }
 
-static void ball_paddles_collision_check(GameState *const state, vec2 new_pos) {
+static GameStateEvent ball_paddles_collision_check(GameState *const state, vec2 new_pos) {
     static const float possible_y_directions[] = {
         0.0f,
         0.5f,
@@ -215,7 +225,7 @@ static void ball_paddles_collision_check(GameState *const state, vec2 new_pos) {
     if (inside_paddle) {
         ball->dirv[0] *= -1.0f;
         ball->dirv[1] = possible_y_directions[se_rand_int() % 3];
-        return;
+        return GSEVENT_BALL_DIR_CHANGE;
     };
 
     collision = se_line_segments_intersect(
@@ -232,10 +242,13 @@ static void ball_paddles_collision_check(GameState *const state, vec2 new_pos) {
         state->ball.dirv[0] *= -1.0f;
         state->ball.dirv[1] = possible_y_directions[se_rand_int() % 3];
         // state->ball.dirv[1] = 0.0f;
+        return GSEVENT_BALL_DIR_CHANGE;
     }
+
+    return GSEVENT_NONE;
 }
 
-static void ball_move(GameState *const state, const double delta) {
+static GameStateEvent ball_move(GameState *const state, const double delta) {
     vec2 ball_movement;
     const float ball_movement_scale = delta * state->ball.speed;
     // glmc_vec2_normalize(state->ball.dirv);
@@ -244,12 +257,15 @@ static void ball_move(GameState *const state, const double delta) {
     vec2 new_pos;
     glmc_vec2_add(state->ball.pos, ball_movement, new_pos);
 
-    if (ball_bounds_collision_check(state, new_pos)) {
-        return;
+    GameStateEvent event = ball_bounds_collision_check(state, new_pos);
+    if (event != GSEVENT_NONE) {
+        return event;
     }
-    ball_paddles_collision_check(state, new_pos);
+    event = ball_paddles_collision_check(state, new_pos);
 
     glmc_vec2_copy(new_pos, state->ball.pos);
+
+    return event;
 }
 
 void GameState_paddle_l_change_dir(GameState *const state, const SEDirection dir) {
@@ -282,7 +298,7 @@ void GameState_paddle_r_change_dir(GameState *const state, const SEDirection dir
     }
 }
 
-void GameState_update(GameState *const state, const double delta) {
+GameStateEvent GameState_update(GameState *const state, const double delta) {
     // Paddles
     vec2 paddle_movement;
     const float paddle_movement_scale = delta * state->paddle_l.speed;
@@ -298,7 +314,24 @@ void GameState_update(GameState *const state, const double delta) {
     paddle_collision_check(&state->paddle_r);
 
     // Ball
-    ball_move(state, delta);
+    const GameStateEvent event = ball_move(state, delta);
+    switch (event) {
+    case GSEVENT_L_SCORE:
+        state->score_l++;
+        game_next_round(state);
+        return event;
+        break;
+    case GSEVENT_R_SCORE:
+        state->score_r++;
+        game_next_round(state);
+        return event;
+        break;
+    case GSEVENT_NONE:
+    case GSEVENT_BALL_DIR_CHANGE:
+        break;
+    }
+
+    return event;
 }
 
 SEDirection GameState_paddle_l_dir_get(const GameState *const state) {

+ 16 - 1
src/common/game.h

@@ -7,6 +7,20 @@
 
 #include "scufeng/engine.h"
 
+#define XGameStateEvent(X) \
+    X(GSEVENT_NONE) \
+    X(GSEVENT_L_SCORE) \
+    X(GSEVENT_R_SCORE) \
+    X(GSEVENT_BALL_DIR_CHANGE)
+
+#define X(evt) evt,
+
+typedef enum {
+    XGameStateEvent(X)
+} GameStateEvent;
+
+#undef X
+
 typedef struct {
     vec2 pos;
     float w;
@@ -36,13 +50,14 @@ typedef struct {
     uint8_t score_r;
 } GameState;
 
+const char *GameStateEvent_to_str(const GameStateEvent evt);
 GameState GameState_new(
     const GameStateParameters params,
     const bool ai_enabled
 );
 void GameState_paddle_l_change_dir(GameState *const state, const SEDirection dir);
 void GameState_paddle_r_change_dir(GameState *const state, const SEDirection dir);
-void GameState_update(GameState *const state, const double delta);
+GameStateEvent GameState_update(GameState *const state, const double delta);
 SEDirection GameState_paddle_l_dir_get(const GameState *const state);
 
 extern const GameStateParameters game_default_params;

+ 18 - 0
src/common/networking.c

@@ -9,6 +9,18 @@
 
 static const uint8_t fb = 0xfb;
 
+const char *NetMsgType_to_str(const NetMsgType type) {
+#define X(msg) \
+    case msg: \
+        return #msg;
+
+    switch (type) {
+        XNetMsgType(X)
+    }
+
+#undef X
+}
+
 size_t NetMsgType_to_buf_size(const NetMsgType type) {
     switch (type) {
     case NET_MSG_PADDLE_CHANGE_DIR:
@@ -21,6 +33,10 @@ size_t NetMsgType_to_buf_size(const NetMsgType type) {
         return sizeof(IsPlayer1Msg);
     case NET_MSG_GAME_START:
         return 0;
+    case NET_MSG_BALL_UPDATE:
+        return sizeof(BallUpdateMsg);
+    case NET_MSG_SCORE_UPDATE:
+        return sizeof(ScoreUpdateMsg);
     }
 
     LOG_FATALF("Invalid msg type: %u", type);
@@ -91,6 +107,8 @@ bool recv_msg_type(FILE *s, NetMsgType *const type) {
     case NET_MSG_GAME_START:
     case NET_MSG_PADDLE_CHANGE_DIR:
     case NET_MSG_IS_PLAYER_1:
+    case NET_MSG_BALL_UPDATE:
+    case NET_MSG_SCORE_UPDATE:
         valid_type = true;
         break;
     }

+ 27 - 5
src/common/networking.h

@@ -8,14 +8,23 @@
 #define SEND_FRAME_MANGLE ((uint8_t) 10)
 #define RECV_FRAME_MANGLE (-(SEND_FRAME_MANGLE))
 
+#define XNetMsgType(X) \
+    X(NET_MSG_PADDLE_CHANGE_DIR) \
+    X(NET_MSG_PADDLE_POS) \
+    X(NET_MSG_GAME_STATE) \
+    X(NET_MSG_GAME_START) \
+    X(NET_MSG_IS_PLAYER_1) \
+    X(NET_MSG_BALL_UPDATE) \
+    X(NET_MSG_SCORE_UPDATE) \
+
+#define X(msg) msg,
+
 typedef enum : uint8_t {
-    NET_MSG_PADDLE_CHANGE_DIR,
-    NET_MSG_PADDLE_POS,
-    NET_MSG_GAME_STATE,
-    NET_MSG_GAME_START,
-    NET_MSG_IS_PLAYER_1
+    XNetMsgType(X)
 } NetMsgType;
 
+#undef X
+
 typedef struct [[gnu::packed]] {
     uint8_t dir;     // SEDirection
 } PaddleChangeDirMsg;
@@ -27,10 +36,23 @@ typedef struct [[gnu::packed]] {
     float r_dirv_y;
 } PaddleUpdateMsg;
 
+typedef struct [[gnu::packed]] {
+    float ball_pos_x;
+    float ball_pos_y;
+    float ball_dirv_x;
+    float ball_dirv_y;
+} BallUpdateMsg;
+
+typedef struct [[gnu::packed]] {
+    uint8_t l_score;
+    uint8_t r_score;
+} ScoreUpdateMsg;
+
 typedef struct [[gnu::packed]] {
     uint8_t player1; // bool
 } IsPlayer1Msg;
 
+const char *NetMsgType_to_str(const NetMsgType type);
 size_t NetMsgType_to_buf_size(const NetMsgType type);
 void send_msg(FILE *s, const NetMsgType type, const uint8_t *const buf);
 bool recv_msg_type(FILE *s, NetMsgType *const type);

+ 8 - 0
src/common/util.c

@@ -0,0 +1,8 @@
+#include "scufcont/scufcont.h"
+#include "scufeng/log.h"
+
+void assert_scufcont_success(const ScufContResult res) {
+    if (res != SCUFCONT_SUCCESS) {
+        LOG_FATALF("Scufcont operation failed: %s", ScufContResult_to_str(res));
+    }
+}

+ 5 - 0
src/common/util.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#include "scufcont/scufcont.h"
+
+void assert_scufcont_success(const ScufContResult res);

+ 24 - 1
src/game/MultiPlayer.c

@@ -120,9 +120,26 @@ static void handle_game_state_msg(MultiPlayer *const mp) {
     GameStateMsg_to_GameState(&msg, &mp->state);
 }
 
+static void handle_ball_update_msg(MultiPlayer *const mp) {
+    BallUpdateMsg msg;
+    recv_msg(mp->conn_s, NET_MSG_BALL_UPDATE, (uint8_t *) &msg);
+    mp->state.ball.pos[0] = msg.ball_pos_x;
+    mp->state.ball.pos[1] = msg.ball_pos_y;
+    mp->state.ball.dirv[0] = msg.ball_dirv_x;
+    mp->state.ball.dirv[1] = msg.ball_dirv_y;
+}
+
+static void handle_score_update_msg(MultiPlayer *const mp) {
+    ScoreUpdateMsg msg;
+    recv_msg(mp->conn_s, NET_MSG_SCORE_UPDATE, (uint8_t *) &msg);
+    mp->state.score_l = msg.l_score;
+    mp->state.score_r = msg.r_score;
+}
+
 void MultiPlayer_tick(MultiPlayer *const mp, const double delta) {
     NetMsgType msg_type;
-    if (recv_msg_type(mp->conn_s, &msg_type)) {
+    while (recv_msg_type(mp->conn_s, &msg_type)) {
+        // LOGF("Got message: %s", NetMsgType_to_str(msg_type));
         switch (msg_type) {
         case NET_MSG_PADDLE_CHANGE_DIR:
             break;
@@ -135,6 +152,12 @@ void MultiPlayer_tick(MultiPlayer *const mp, const double delta) {
         case NET_MSG_IS_PLAYER_1:
         case NET_MSG_GAME_START:
             break;
+        case NET_MSG_BALL_UPDATE:
+            handle_ball_update_msg(mp);
+            break;
+        case NET_MSG_SCORE_UPDATE:
+            handle_score_update_msg(mp);
+            break;
         }
     }
 

+ 70 - 52
src/server/server.c

@@ -15,6 +15,7 @@
 
 #include "game.h"
 #include "networking.h"
+#include "util.h"
 #include "GameState_net.h"
 
 #define SERVER_HOST "127.0.0.1"
@@ -25,59 +26,22 @@ typedef enum {
     SS_GAME
 } ServerState;
 
-typedef struct {
-    NetMsgType type;
-    void *data;
-} OutgoingMsg;
-
-SCDARRAY_HEADER(OutgoingMsg, OutgoingMsg)
+SCDARRAY_HEADER(NetMsgType, NetMsgType)
 
 static FILE *p1_conn = nullptr;
 static FILE *p2_conn = nullptr;
 static ServerState server_state = SS_LOBBY;
 static GameState game_state;
-static SCDArray_OutgoingMsg out_msgs;
-
-SCDARRAY_IMPL(OutgoingMsg, OutgoingMsg)
-
-static OutgoingMsg OutgoingMsg_alloc(const NetMsgType type, void *const data) {
-    const size_t data_size = NetMsgType_to_buf_size(type);
-
-    OutgoingMsg msg;
-    msg.type = type;
-    if (data != nullptr) {
-        msg.data = malloc(data_size);
-        memcpy(msg.data, data, data_size);
-    } else {
-        msg.data = nullptr;
-    }
-    return msg;
-}
+static SCDArray_NetMsgType out_msgs;
 
-static void OutgoingMsg_free(OutgoingMsg *const msg) {
-    if (msg->data != nullptr) {
-        free(msg->data);
-        msg->data = nullptr;
-    }
-}
-
-static void push_out_msg(const NetMsgType type, void *const data) {
-    const OutgoingMsg msg = OutgoingMsg_alloc(type, data);
-    SCDArray_OutgoingMsg_push_back(&out_msgs, &msg);
-}
-
-static OutgoingMsg pop_out_msg() {
-    OutgoingMsg *msg = SCDArray_OutgoingMsg_at(&out_msgs, out_msgs.len - 1);
-    const OutgoingMsg ret = *msg;
-    SCDArray_OutgoingMsg_remove(&out_msgs, out_msgs.len - 1);
-    return ret;
-}
+SCDARRAY_IMPL(NetMsgType, NetMsgType)
 
 static void send_out_msgs() {
     for (size_t i = 0; i < out_msgs.len; i++) {
-        OutgoingMsg out_msg = pop_out_msg();
-        switch (out_msg.type) {
+        const NetMsgType *const type = SCDArray_NetMsgType_at(&out_msgs, i);
+        switch (*type) {
         case NET_MSG_PADDLE_POS:
+        {
             // p1
             PaddleUpdateMsg msg = {
                 .l_pos_y = game_state.paddle_l.pos[1],
@@ -93,25 +57,54 @@ static void send_out_msgs() {
             msg.r_dirv_y = game_state.paddle_l.dirv[1];
             send_msg(p2_conn, NET_MSG_PADDLE_POS, (uint8_t *) &msg);
             break;
+        }
         case NET_MSG_PADDLE_CHANGE_DIR:
         case NET_MSG_GAME_STATE:
         case NET_MSG_GAME_START:
         case NET_MSG_IS_PLAYER_1:
             break;
+        case NET_MSG_BALL_UPDATE:
+        {
+            BallUpdateMsg msg = {
+                .ball_pos_x = game_state.ball.pos[0],
+                .ball_pos_y = game_state.ball.pos[1],
+                .ball_dirv_x = game_state.ball.dirv[0],
+                .ball_dirv_y = game_state.ball.dirv[1],
+            };
+            send_msg(p1_conn, NET_MSG_BALL_UPDATE, (uint8_t *) &msg);
+
+            msg.ball_pos_x = 1.0f - msg.ball_pos_x;
+            msg.ball_dirv_x *= -1.0f;
+            send_msg(p2_conn, NET_MSG_BALL_UPDATE, (uint8_t *) &msg);
+            break;
         }
+        case NET_MSG_SCORE_UPDATE:
+        {
+            ScoreUpdateMsg msg = {
+                .l_score = game_state.score_l,
+                .r_score = game_state.score_r
+            };
+            send_msg(p1_conn, NET_MSG_SCORE_UPDATE, (uint8_t *) &msg);
 
-        OutgoingMsg_free(&out_msg);
+            msg.l_score = game_state.score_r;
+            msg.r_score = game_state.score_l;
+            send_msg(p2_conn, NET_MSG_SCORE_UPDATE, (uint8_t *) &msg);
+            break;
+        }
+        }
     }
+
+    SCDArray_NetMsgType_clear_no_resize(&out_msgs);
 }
 
-static void send_game_state(FILE *conn) {
+static void send_game_state(FILE *conn, const bool p1) {
     GameStateMsg msg;
-    GameState_to_GameStateMsg(&game_state, &msg);
+    GameState_to_GameStateMsg(&game_state, &msg, p1);
     send_msg(conn, NET_MSG_GAME_STATE, (uint8_t *) &msg);
 }
 
 static void server_lobby(const int sock) {
-    SCDArray_OutgoingMsg_alloc(&out_msgs);
+    assert_scufcont_success(SCDArray_NetMsgType_alloc(&out_msgs));
     game_state = GameState_new(game_default_params, false);
 
     int player1 = accept(sock, nullptr, nullptr);
@@ -151,7 +144,7 @@ static void server_lobby(const int sock) {
     send_msg(p2_conn, NET_MSG_IS_PLAYER_1, (uint8_t *) &p1_msg);
 }
 
-static void handle_paddle_change_dir_msg(FILE* conn, const bool p1) {
+static void handle_paddle_change_dir_msg(FILE *conn, const bool p1) {
     PaddleChangeDirMsg msg;
     recv_msg(conn, NET_MSG_PADDLE_CHANGE_DIR, (uint8_t *) &msg);
     SEDirection dir = (SEDirection) msg.dir;
@@ -160,7 +153,8 @@ static void handle_paddle_change_dir_msg(FILE* conn, const bool p1) {
     } else {
         GameState_paddle_r_change_dir(&game_state, dir);
     }
-    push_out_msg(NET_MSG_PADDLE_POS, nullptr);
+    const NetMsgType type = NET_MSG_PADDLE_POS;
+    assert_scufcont_success(SCDArray_NetMsgType_push_back(&out_msgs, &type));
 }
 
 static void handle_player_input(const bool p1) {
@@ -175,6 +169,8 @@ static void handle_player_input(const bool p1) {
         case NET_MSG_GAME_STATE:
         case NET_MSG_GAME_START:
         case NET_MSG_IS_PLAYER_1:
+        case NET_MSG_BALL_UPDATE:
+        case NET_MSG_SCORE_UPDATE:
             break;
         }
     }
@@ -185,8 +181,8 @@ static void server_game() {
 
     send_msg(p1_conn, NET_MSG_GAME_START, nullptr);
     send_msg(p2_conn, NET_MSG_GAME_START, nullptr);
-    send_game_state(p1_conn);
-    send_game_state(p2_conn);
+    send_game_state(p1_conn, true);
+    send_game_state(p2_conn, false);
 
     double last_frame_time_s = 0.0;
     while (true) {
@@ -200,7 +196,29 @@ static void server_game() {
         handle_player_input(false);
 
         // Tick
-        GameState_update(&game_state, delta);
+        const GameStateEvent gs_event = GameState_update(&game_state, delta);
+        // if (gs_event != GSEVENT_NONE) {
+        //     LOGF("GameStateEvent: %s", GameStateEvent_to_str(gs_event));
+        // }
+        switch (gs_event) {
+        case GSEVENT_BALL_DIR_CHANGE:
+        {
+            const NetMsgType type = NET_MSG_BALL_UPDATE;
+            assert_scufcont_success(SCDArray_NetMsgType_push_back(&out_msgs, &type));
+            break;
+        }
+        case GSEVENT_NONE:
+            break;
+        case GSEVENT_L_SCORE:
+        case GSEVENT_R_SCORE:
+        {
+            NetMsgType type = NET_MSG_SCORE_UPDATE;
+            assert_scufcont_success(SCDArray_NetMsgType_push_back(&out_msgs, &type));
+            type = NET_MSG_BALL_UPDATE;
+            assert_scufcont_success(SCDArray_NetMsgType_push_back(&out_msgs, &type));
+            break;
+        }
+        }
 
         // Player output
         send_out_msgs();