ソースを参照

Updated Scufeng. Changes to text rendering

Aaro Saila 2 週間 前
親
コミット
4ded557415

+ 88 - 69
src/game/MainMenu.c

@@ -1,49 +1,58 @@
+#include <string.h>
+
 #include "scufeng/engine.h"
 #include "scufeng/Mesh.h"
 #include "scufeng/log.h"
 #include "cglm/vec3.h"
-#include "cglm/call/vec3.h"
 #include "cglm/io.h"
 
 #include "MainMenu.h"
 #include "game_common.h"
 #include "scufeng/text.h"
 
-static LoginPrompt LoginPrompt_alloc(const unsigned int screen_w, const unsigned int screen_h) {
-    LoginPrompt lp = {
-        .bg = SEMesh_rect_color_alloc((vec4) { 0.05f, 0.05f, 0.05f, 1.0f }),
-        .username_text = SEMesh_string_alloc("Username:", GLM_VEC3_ONE),
-        .username_input = SETextInput_alloc(
-            "",
-            0,
-            0,
-            0,
-            GLM_VEC3_ONE,
-            (vec4) { 0.0025f, 0.0025f, 0.0025f, 1.0f },
-            TI_FIXED_WIDTH
-        ),
-        .password_text = SEMesh_string_alloc("Password:", GLM_VEC3_ONE),
-        .password_input = SETextInput_alloc(
-            "",
-            0,
-            0,
-            0,
-            GLM_VEC3_ONE,
-            (vec4) { 0.0025f, 0.0025f, 0.0025f, 1.0f },
-            TI_FIXED_WIDTH
-        ),
-        .exit_text = SEMesh_string_alloc("X", GLM_VEC3_ONE),
-        .login_text = SEMesh_string_alloc("Login", GLM_VEC3_ONE)
-    };
+#define SE_RESULT_CHECK(res) \
+    if (res != SE_SUCCESS) { \
+        return res;          \
+    }
 
+static SEResult LoginPrompt_alloc(LoginPrompt *const lp, const unsigned int screen_w, const unsigned int screen_h) {
+    lp->bg = SEMesh_rect_color_alloc((vec4) { 0.05f, 0.05f, 0.05f, 1.0f });
     vec2 bg_dims = { screen_w * 0.75f, screen_h * 0.75f };
     vec2 bg_pos = { screen_w * 0.5f, screen_h * 0.5f };
-    lp.bg.transform.scale[0] = bg_dims[0];
-    lp.bg.transform.scale[1] = bg_dims[1];
-    lp.bg.transform.pos[0] = bg_pos[0];
-    lp.bg.transform.pos[1] = bg_pos[1];
+    lp->bg.transform.scale[0] = bg_dims[0];
+    lp->bg.transform.scale[1] = bg_dims[1];
+    lp->bg.transform.pos[0] = bg_pos[0];
+    lp->bg.transform.pos[1] = bg_pos[1];
     vec4 bg_bbox;
-    SEMesh_bbox_get_2d_v(&lp.bg, bg_bbox);
+    SEMesh_bbox_get_2d_v(&lp->bg, bg_bbox);
+
+    SEResult res = SEMesh_string_alloc(&lp->username_text, "Username:", strlen("Username:"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+
+    lp->username_input = SETextInput_alloc(
+        "",
+        0,
+        0,
+        0,
+        GLM_VEC3_ONE,
+        (vec4) { 0.0025f, 0.0025f, 0.0025f, 1.0f },
+        TI_FIXED_WIDTH
+    ),
+    SETextInput_fixed_width_set(&lp->username_input, bg_dims[0] * 0.25f);
+
+    res = SEMesh_string_alloc(&lp->password_text, "Password:", strlen("Password:"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+
+    lp->password_input = SETextInput_alloc(
+        "",
+        0,
+        0,
+        0,
+        GLM_VEC3_ONE,
+        (vec4) { 0.0025f, 0.0025f, 0.0025f, 1.0f },
+        TI_FIXED_WIDTH
+    );
+    SETextInput_fixed_width_set(&lp->password_input, bg_dims[0] * 0.25f);
 
     puts("bg_dims");
     glm_vec2_print(bg_dims, stdout);
@@ -52,14 +61,16 @@ static LoginPrompt LoginPrompt_alloc(const unsigned int screen_w, const unsigned
     puts("bg_bbox");
     glm_vec4_print(bg_bbox, stdout);
 
-    lp.exit_text.transform.pos[0] = bg_bbox[0] + bg_dims[0] * 0.025f; 
-    lp.exit_text.transform.pos[1] = bg_bbox[1] + bg_dims[1] * 0.05f;
-    glm_vec3_print(lp.exit_text.transform.pos, stdout);
+    res = SEMesh_string_alloc(&lp->exit_text, "X", 1, GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+    lp->exit_text.transform.pos[0] = bg_bbox[0] + bg_dims[0] * 0.025f;
+    lp->exit_text.transform.pos[1] = bg_bbox[1] + bg_dims[1] * 0.05f;
+    glm_vec3_print(lp->exit_text.transform.pos, stdout);
 
-    SETextInput_fixed_width_set(&lp.username_input, bg_dims[0] * 0.25f);
-    SETextInput_fixed_width_set(&lp.password_input, bg_dims[0] * 0.25f);
+    res = SEMesh_string_alloc(&lp->login_text, "Login", strlen("Login"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
 
-    return lp;
+    return SE_SUCCESS;
 }
 
 static void LoginPrompt_render(LoginPrompt *const lp) {
@@ -79,37 +90,45 @@ static void LoginPrompt_render(LoginPrompt *const lp) {
     SETextInput_render_multiple_2d(tis, sizeof(tis) / sizeof(tis[0]));
 }
 
-MainMenu MainMenu_alloc(const unsigned int screen_w, const unsigned int screen_h) {
-    MainMenu mm;
-
-    mm.title_text = SEMesh_string_alloc("Pong", GLM_VEC3_ONE);
-    SEMesh_rect_textured_scale_set(&mm.title_text, screen_w * 0.16f, 0.0f);
-    mm.title_text.transform.pos[0] = screen_w / 2.0f;
-    mm.title_text.transform.pos[1] = screen_h * 0.1f;
-
-    mm.login_text = SEMesh_string_alloc("Login", GLM_VEC3_ONE);
-    SEMesh_rect_textured_scale_set(&mm.login_text, screen_w * 0.06f, 0.0f);
-    mm.login_text.transform.pos[0] = screen_w / 6.0f;
-    mm.login_text.transform.pos[1] = screen_h * 0.2f;
-
-    mm.sp_text = SEMesh_string_alloc("Singleplayer", GLM_VEC3_ONE);
-    SEMesh_rect_textured_scale_set(&mm.sp_text, screen_w * 0.25f, 0.0f);
-    mm.sp_text.transform.pos[0] = screen_w * 0.25f;
-    mm.sp_text.transform.pos[1] = screen_h * 0.5f;
-
-    mm.mp_text = SEMesh_string_alloc("Multiplayer", GLM_VEC3_ONE);
-    SEMesh_rect_textured_scale_set(&mm.mp_text, mm.sp_text.transform.scale[0], 0.0f);
-    mm.mp_text.transform.pos[0] = screen_w - mm.sp_text.transform.pos[0];
-    mm.mp_text.transform.pos[1] = mm.sp_text.transform.pos[1];
-
-    mm.quit_text = SEMesh_string_alloc("Quit", GLM_VEC3_ONE);
-    SEMesh_rect_textured_scale_set(&mm.quit_text, 0.0f, mm.title_text.transform.scale[1] / 2.0f);
-    mm.quit_text.transform.pos[0] = screen_w / 2.0f;
-    mm.quit_text.transform.pos[1] = screen_h * 0.8f;
-
-    mm.login_prompt = LoginPrompt_alloc(screen_w, screen_h);
-
-    return mm;
+SEResult MainMenu_alloc(
+    MainMenu *const mm,
+    const unsigned int screen_w,
+    const unsigned int screen_h
+) {
+    SEResult res = SEMesh_string_alloc(&mm->title_text, "Pong", strlen("Pong"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+    SEMesh_rect_textured_scale_set(&mm->title_text, screen_w * 0.16f, 0.0f);
+    mm->title_text.transform.pos[0] = screen_w / 2.0f;
+    mm->title_text.transform.pos[1] = screen_h * 0.1f;
+
+    res = SEMesh_string_alloc(&mm->login_text, "Login", strlen("Login"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+    SEMesh_rect_textured_scale_set(&mm->login_text, screen_w * 0.1f, 0.0f);
+    mm->login_text.transform.pos[0] = screen_w / 6.0f;
+    mm->login_text.transform.pos[1] = screen_h * 0.2f;
+
+    res = SEMesh_string_alloc(&mm->sp_text, "Singleplayer", strlen("Singleplayer"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+    SEMesh_rect_textured_scale_set(&mm->sp_text, screen_w * 0.30f, 0.0f);
+    mm->sp_text.transform.pos[0] = screen_w * 0.25f;
+    mm->sp_text.transform.pos[1] = screen_h * 0.5f;
+
+    res = SEMesh_string_alloc(&mm->mp_text, "Multiplayer", strlen("Multiplayer"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+    SEMesh_rect_textured_scale_set(&mm->mp_text, mm->sp_text.transform.scale[0], 0.0f);
+    mm->mp_text.transform.pos[0] = screen_w - mm->sp_text.transform.pos[0];
+    mm->mp_text.transform.pos[1] = mm->sp_text.transform.pos[1];
+
+    res = SEMesh_string_alloc(&mm->quit_text, "Quit", strlen("Quit"), GLM_VEC3_ONE);
+    SE_RESULT_CHECK(res);
+    SEMesh_rect_textured_scale_set(&mm->quit_text, mm->title_text.transform.scale[0] * 0.6f, 0.0f);
+    mm->quit_text.transform.pos[0] = screen_w / 2.0f;
+    mm->quit_text.transform.pos[1] = screen_h * 0.8f;
+
+    res = LoginPrompt_alloc(&mm->login_prompt, screen_w, screen_h);
+    SE_RESULT_CHECK(res);
+
+    return res;
 }
 
 static Screen MainMenu_input_handle(MainMenu *const mm) {

+ 10 - 1
src/game/MainMenu.h

@@ -24,5 +24,14 @@ typedef struct {
     LoginPrompt login_prompt;
 } MainMenu;
 
-MainMenu MainMenu_alloc(const unsigned int screen_w, const unsigned int screen_h);
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+SEResult MainMenu_alloc(
+    MainMenu *const mm,
+    const unsigned int screen_w,
+    const unsigned int screen_h
+);
+
 Screen MainMenu_tick(MainMenu *const mm);

+ 15 - 5
src/game/MultiPlayer.c

@@ -9,6 +9,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "scufeng/engine.h"
 #include "scufeng/log.h"
 
 #include "networking.h"
@@ -147,11 +148,17 @@ bool MultiPlayer_alloc(
         return false;
     }
 
-    SE_LOG("Game started");
-
     mp->screen_w = screen_w;
     mp->screen_h = screen_h;
-    mp->shapes = GameShapes_alloc(screen_w, screen_h, game_default_params);
+    SEResult se_res = GameShapes_alloc(&mp->shapes, screen_w, screen_h, game_default_params);
+    if (se_res != SE_SUCCESS) {
+        fclose(mp->conn);
+        close(mp->sock);
+        SE_LOG_ERRORF("Failed to allocate GameShapes: %s", SEResult_to_str(se_res));
+        return false;
+    }
+
+    SE_LOG("Game started");
 
     return mp;
 }
@@ -297,8 +304,11 @@ Screen MultiPlayer_tick(MultiPlayer *const mp, const double delta) {
     }
 
     GameState_update(&mp->state, delta);
-    GameShapes_update(&mp->shapes, &mp->state, mp->screen_w, mp->screen_h);
-    GameShapes_render(&mp->shapes, &mp->state, mp->screen_w, mp->screen_h);
+    SEResult res = GameShapes_update(&mp->shapes, &mp->state, mp->screen_w, mp->screen_h);
+    if (res != SE_SUCCESS) {
+        SE_LOG_FATALF("[MultiPlayer_tick] GameShapes_update failed: %s", SEResult_to_str(res));
+    }
+    GameShapes_render(&mp->shapes);
 
     return PONG_MULTIPLAYER;
 }

+ 18 - 28
src/game/SinglePlayer.c

@@ -1,42 +1,29 @@
-#include "scufeng/Mesh.h"
+#include "scufeng/engine.h"
+#include "scufeng/log.h"
 
 #include "game.h"
 #include "SinglePlayer.h"
 #include "game_common.h"
 
-SinglePlayer SinglePlayer_alloc(
+SEResult SinglePlayer_alloc(
+    SinglePlayer *const sp,
     unsigned int screen_w,
     unsigned int screen_h,
     GameStateParameters params
 ) {
-    SinglePlayer sp = {
-        .screen_w = screen_w,
-        .screen_h = screen_h,
-        .state = GameState_new(params, true),
-        .shapes = GameShapes_alloc(screen_w, screen_h, params)
-    };
-
-    return sp;
-}
-
-SinglePlayer SinglePlayer_alloc_default(
-    unsigned int screen_w,
-    unsigned int screen_h
-) {
-    SinglePlayer sp = {
-        .screen_w = screen_w,
-        .screen_h = screen_h,
-        .state = GameState_new(game_default_params, true),
-        .shapes = GameShapes_alloc(screen_w, screen_h, game_default_params)
-    };
+    sp->screen_w = screen_w;
+    sp->screen_h = screen_h;
+    sp->state = GameState_new(params, true);
+    SEResult res = GameShapes_alloc(&sp->shapes, screen_w, screen_h, params);
+    if (res != SE_SUCCESS) {
+        return res;
+    }
 
-    return sp;
+    return SE_SUCCESS;
 }
 
 void SinglePlayer_free(SinglePlayer *sp) {
-    SEMesh_free(&sp->shapes.paddle_l);
-    SEMesh_free(&sp->shapes.paddle_r);
-    SEMesh_free(&sp->shapes.ball);
+    GameShapes_free(&sp->shapes);
 }
 
 static bool key_input_handle(GameState *const state) {
@@ -71,8 +58,11 @@ Screen SinglePlayer_tick(SinglePlayer *const sp, const double delta) {
         break;
     }
 
-    GameShapes_update(&sp->shapes, &sp->state, sp->screen_w, sp->screen_h);
-    GameShapes_render(&sp->shapes, &sp->state, sp->screen_w, sp->screen_h);
+    const SEResult res = GameShapes_update(&sp->shapes, &sp->state, sp->screen_w, sp->screen_h);
+    if (res != SE_SUCCESS) {
+        SE_LOG_FATALF("[SinglePlayer_tick] GameShapes_update failed: %s", SEResult_to_str(res));
+    }
+    GameShapes_render(&sp->shapes);
 
     return PONG_SINGLEPLAYER;
 }

+ 7 - 5
src/game/SinglePlayer.h

@@ -10,14 +10,16 @@ typedef struct {
     GameShapes shapes;
 } SinglePlayer;
 
-SinglePlayer SinglePlayer_alloc(
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+SEResult SinglePlayer_alloc(
+    SinglePlayer *const sp,
     unsigned int screen_w,
     unsigned int screen_h,
     GameStateParameters params
 );
-SinglePlayer SinglePlayer_alloc_default(
-    unsigned int screen_w,
-    unsigned int screen_h
-);
+
 void SinglePlayer_free(SinglePlayer *sp);
 Screen SinglePlayer_tick(SinglePlayer *const sp, const double delta);

+ 112 - 51
src/game/game_common.c

@@ -1,10 +1,13 @@
-#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
 #include <unistd.h>
+#include <stdlib.h>
 
 #include "cglm/vec4.h"
 #include "cglm/vec3.h"
 #include "scufeng/Mesh.h"
-#include "scufeng/text.h"
+#include "scufeng/engine.h"
+#include "scufeng/log.h"
 
 #include "game_common.h"
 #include "game.h"
@@ -21,87 +24,138 @@ static void pos_convert(
     render_pos[1] = state_pos[1] * screen_h;
 }
 
-void GameShapes_update(
+static void score_update_shape(
+    SEMesh *const score_mesh,
+    const bool left,
+    const unsigned int screen_w,
+    const unsigned int screen_h
+) {
+    const float x = left ? 0.25f : 1.0f - 0.25f;
+    SEMesh_rect_textured_scale_set(score_mesh, 0.0f, screen_h * 0.1f);
+    score_mesh->transform.pos[0] = screen_w * x;
+    score_mesh->transform.pos[1] = score_mesh->transform.scale[1] / 2.0f + screen_h * 0.1f;
+}
+
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+static SEResult score_update(
     GameShapes *const shapes,
     const GameState *const state,
+    const bool left,
     const unsigned int screen_w,
     const unsigned int screen_h
 ) {
+    uint8_t score;
+    SEMesh *mesh = nullptr;
+
+    if (left) {
+        score = state->score_l;
+        mesh = &shapes->score_l;
+    } else {
+        score = state->score_r;
+        mesh = &shapes->score_r;
+    }
+
+    const unsigned long mesh_score = strtoul(SEMesh_string_c_str(mesh), nullptr, 10);
+    if (mesh_score != (unsigned long) score) {
+        char buf[4]; // Score max is 255 since GameState.score_x is a uint8_t
+        snprintf(buf, 4, "%u", score);
+        SEResult res = SEMesh_string_text_replace(mesh, buf, strlen(buf), mesh->string.color);
+        if (res != SE_SUCCESS) {
+            return res;
+        }
+        score_update_shape(mesh, left, screen_w, screen_h);
+    }
+
+    return SE_SUCCESS;
+}
 
+SEResult GameShapes_update(
+    GameShapes *const shapes,
+    const GameState *const state,
+    const unsigned int screen_w,
+    const unsigned int screen_h
+) {
     // Paddles
     pos_convert(shapes->paddle_l.transform.pos, state->paddle_l.pos, screen_w, screen_h);
     pos_convert(shapes->paddle_r.transform.pos, state->paddle_r.pos, screen_w, screen_h);
 
     // Ball
     pos_convert(shapes->ball.transform.pos, state->ball.pos, screen_w, screen_h);
+
+    // Scores
+    SEResult res = score_update(shapes, state, true, screen_w, screen_h);
+    if (res != SE_SUCCESS) {
+        return res;
+    }
+    res = score_update(shapes, state, false, screen_w, screen_h);
+    if (res != SE_SUCCESS) {
+        return res;
+    }
+
+    return SE_SUCCESS;
 }
 
-GameShapes GameShapes_alloc(
+SEResult GameShapes_alloc(
+    GameShapes *const shapes,
     unsigned int screen_w,
     unsigned int screen_h,
     GameStateParameters params
 ) {
-    GameShapes shapes = {
-        .paddle_l = SEMesh_rect_color_alloc(GLM_VEC4_ONE),
-        .paddle_r = SEMesh_rect_color_alloc(GLM_VEC4_ONE),
-        .ball = SEMesh_rect_color_alloc(GLM_VEC4_ONE),
-    };
-    shapes.paddle_l.transform.scale[0] = screen_w * params.paddles_w;
-    shapes.paddle_l.transform.scale[1] = screen_h * params.paddles_h;
-    shapes.paddle_l.transform.pos[0] = screen_w * params.paddle_l_x;
-    shapes.paddle_l.transform.pos[1] = screen_h / 2.0f;
-
-    shapes.paddle_r.transform.scale[0] = screen_w * params.paddles_w;
-    shapes.paddle_r.transform.scale[1] = screen_h * params.paddles_h;
-    shapes.paddle_r.transform.pos[0] = screen_w * (1.0f - params.paddle_l_x);
-    shapes.paddle_r.transform.pos[1] = screen_h / 2.0f;
-
-    shapes.ball.transform.scale[0] = screen_w * params.ball_w;
-    shapes.ball.transform.scale[1] = shapes.ball.transform.scale[0];
-    shapes.ball.transform.pos[0] = screen_w * params.ball_pos[0];
-    shapes.ball.transform.pos[1] = screen_h * params.ball_pos[1];
-
-    return shapes;
+    shapes->paddle_l = SEMesh_rect_color_alloc(GLM_VEC4_ONE);
+    shapes->paddle_l.transform.scale[0] = screen_w * params.paddles_w;
+    shapes->paddle_l.transform.scale[1] = screen_h * params.paddles_h;
+    shapes->paddle_l.transform.pos[0] = screen_w * params.paddle_l_x;
+    shapes->paddle_l.transform.pos[1] = screen_h / 2.0f;
+
+    shapes->paddle_r = SEMesh_rect_color_alloc(GLM_VEC4_ONE);
+    shapes->paddle_r.transform.scale[0] = screen_w * params.paddles_w;
+    shapes->paddle_r.transform.scale[1] = screen_h * params.paddles_h;
+    shapes->paddle_r.transform.pos[0] = screen_w * (1.0f - params.paddle_l_x);
+    shapes->paddle_r.transform.pos[1] = screen_h / 2.0f;
+
+    shapes->ball = SEMesh_rect_color_alloc(GLM_VEC4_ONE);
+    shapes->ball.transform.scale[0] = screen_w * params.ball_w;
+    shapes->ball.transform.scale[1] = shapes->ball.transform.scale[0];
+    shapes->ball.transform.pos[0] = screen_w * params.ball_pos[0];
+    shapes->ball.transform.pos[1] = screen_h * params.ball_pos[1];
+
+    SEResult res = SEMesh_string_alloc(&shapes->score_l, "0", 1, GLM_VEC3_ONE);
+    if (res != SE_SUCCESS) {
+        return res;
+    }
+    score_update_shape(&shapes->score_l, true, screen_w, screen_h);
+
+    res = SEMesh_string_alloc(&shapes->score_r, "0", 1, GLM_VEC3_ONE);
+    if (res != SE_SUCCESS) {
+        return res;
+    }
+    score_update_shape(&shapes->score_r, false, screen_w, screen_h);
+
+    return SE_SUCCESS;
 }
 
 void GameShapes_free(GameShapes *const shapes) {
     SEMesh_free(&shapes->paddle_l);
     SEMesh_free(&shapes->paddle_r);
     SEMesh_free(&shapes->ball);
+    SEMesh_free(&shapes->score_l);
+    SEMesh_free(&shapes->score_r);
 }
 
 void GameShapes_render(
-    GameShapes *const shapes,
-    const GameState *const state,
-    const unsigned int screen_w,
-    const unsigned int screen_h
+    GameShapes *const shapes
 ) {
     SEMesh *meshes[] = {
         &shapes->paddle_l,
         &shapes->paddle_r,
-        &shapes->ball
+        &shapes->ball,
+        &shapes->score_l,
+        &shapes->score_r
     };
     SEMesh_render_multiple_2d(meshes, sizeof(meshes) / sizeof(meshes[0]));
-
-    const float score_w = 0.25f;
-    const float score_h = 0.1f;
-    char score_txt[4] = { 0 };
-    snprintf(score_txt, 4, "%d", state->score_l);
-    se_text_render(
-        score_txt,
-        screen_w * score_w,
-        screen_h * score_h,
-        1.0f,
-        GLM_VEC3_ONE
-    );
-    snprintf(score_txt, 4, "%d", state->score_r);
-    se_text_render(
-        score_txt,
-        screen_w * (1.0f - score_w),
-        screen_h * score_h,
-        1.0f,
-        GLM_VEC3_ONE
-    );
 }
 
 void win_lose_render(
@@ -110,10 +164,17 @@ void win_lose_render(
     const unsigned int screen_h
 ) {
     const char *text_str = win ? "You Win!" : "You Lose!";
-    SEMesh text = SEMesh_string_alloc(
+    SEMesh text;
+    SEResult res = SEMesh_string_alloc(
+        &text,
         text_str,
+        strlen(text_str),
         (vec3) { 1.0f, 1.0f, 1.0f }
     );
+    if (res != SE_SUCCESS) {
+        SE_LOG_FATALF("Failed to alloc win/lose string: %s", SEResult_to_str(res));
+    }
+    SEMesh_rect_textured_scale_set(&text, screen_w * 0.25f, 0.0f);
     text.transform.pos[0] = screen_w / 2.0f;
     text.transform.pos[1] = screen_h / 2.0f;
 

+ 16 - 6
src/game/game_common.h

@@ -16,25 +16,35 @@ typedef struct {
     SEMesh paddle_l;
     SEMesh paddle_r;
     SEMesh ball;
+    SEMesh score_l;
+    SEMesh score_r;
 } GameShapes;
 
-void GameShapes_update(
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+SEResult GameShapes_update(
     GameShapes *const shapes,
     const GameState *const state,
     const unsigned int screen_w,
     const unsigned int screen_h
 );
-GameShapes GameShapes_alloc(
+
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+SEResult GameShapes_alloc(
+    GameShapes *const shapes,
     unsigned int screen_w,
     unsigned int screen_h,
     GameStateParameters params
 );
+
 void GameShapes_free(GameShapes *shapes);
 void GameShapes_render(
-    GameShapes *const shapes,
-    const GameState *const state,
-    const unsigned int screen_w,
-    const unsigned int screen_h
+    GameShapes *const shapes
 );
 void win_lose_render(
     const bool win,

+ 16 - 4
src/game/pong.c

@@ -1,14 +1,16 @@
 #include <signal.h>
 
+#include "game.h"
 #include "scufeng/engine.h"
 #include "scufeng/text.h"
+#include "scufeng/log.h"
 
 #include "MainMenu.h"
 #include "SinglePlayer.h"
 #include "MultiPlayer.h"
 #include "src/game/game_common.h"
 
-static const unsigned int screen_w = 1200;
+static const unsigned int screen_w = 800;
 static const unsigned int screen_h = 600;
 
 int main() {
@@ -22,7 +24,11 @@ int main() {
 
     se_clear_color(0.1f, 0.1f, 0.1f, 1.0f);
 
-    MainMenu mm = MainMenu_alloc(screen_w, screen_h);
+    MainMenu mm;
+    SEResult res = MainMenu_alloc(&mm, screen_w, screen_h);
+    if (res != SE_SUCCESS) {
+        SE_LOG_FATALF("Failed to allocate MainMenu: %s", SEResult_to_str(res));
+    }
     SinglePlayer sp;
     MultiPlayer mp;
 
@@ -41,10 +47,16 @@ int main() {
             current_screen = MainMenu_tick(&mm);
             switch (current_screen) {
             case PONG_SINGLEPLAYER:
-                sp = SinglePlayer_alloc_default(
+                res = SinglePlayer_alloc(
+                    &sp,
                     screen_w,
-                    screen_h
+                    screen_h,
+                    game_default_params
                 );
+                if (res != SE_SUCCESS) {
+                    SE_LOG_ERRORF("Failed to alloc SinglePlayer: %d", SEResult_to_str(res));
+                    current_screen = PONG_MAIN_MENU;
+                }
                 break;
             case PONG_MULTIPLAYER:
             {

+ 2 - 2
src/server/server.c

@@ -141,7 +141,7 @@ static bool server_lobby(const int sock) {
     if (player1 == -1) {
         SE_LOG_FATALF("accept failed: %s", strerror(errno));
     }
-    printf("Player 1 connected\n");
+    SE_LOG("Player 1 connected");
 
     p1_conn = fdopen(player1, "r+");
     if (p1_conn == nullptr) {
@@ -161,7 +161,7 @@ static bool server_lobby(const int sock) {
     if (player2 == -1) {
         SE_LOG_FATALF("accept failed: %s", strerror(errno));
     }
-    printf("Player 2 connected\n");
+    SE_LOG("Player 2 connected");
 
     p2_conn = fdopen(player2, "r+");
     if (p2_conn == nullptr) {

+ 1 - 1
subprojects/scufeng-x86_64/GIT_COMMIT

@@ -1 +1 @@
-fd88568b43605f8593514e2e200f1222cc5197d8
+ea601853717bf540ad08143ce2f906f310bdbb3a

+ 14 - 1
subprojects/scufeng-x86_64/include/scufcont/DString.h

@@ -38,6 +38,7 @@ ScufContResult SCDString_alloc_copy(SCDString *const dstr, const SCDString *cons
 ScufContResult SCDString_reserve(SCDString *const dstr, const size_t new_capacity);
 
 void SCDString_free(SCDString *const dstr);
+void SCDString_shrink_to_fit(SCDString *const dstr);
 
 /**
   @param dst Must have been allocated with one of the allocation functions.
@@ -107,7 +108,7 @@ ScufContResult SCDString_char_mod(SCDString *const dstr, const size_t at, const
   @return SCUFCONT_OUT_OF_BOUNDS
   @return SCUFCONT_SUCCESS
 */
-ScufContResult SCDString_char_remove_no_resize(SCDString *const dstr, const size_t at);
+ScufContResult SCDString_char_remove_no_shrink(SCDString *const dstr, const size_t at);
 
 /**
   @return SCUFCONT_OUT_OF_BOUNDS
@@ -122,4 +123,16 @@ ScufContResult SCDString_char_remove(SCDString *const dstr, const size_t at);
 */
 ScufContResult SCDString_char_insert(SCDString *const dstr, const size_t at, const char ch);
 
+/**
+  @return SCUFCONT_OUT_OF_MEMORY
+  @return SCUFCONT_SUCCESS
+*/
+ScufContResult SCDString_replace_c_str_no_shrink(SCDString *const dstr, const char *str, const size_t str_len);
+
+/**
+  @return SCUFCONT_OUT_OF_MEMORY
+  @return SCUFCONT_SUCCESS
+*/
+ScufContResult SCDString_replace_c_str(SCDString *const dstr, const char *str, const size_t str_len);
+
 #endif // SCUFCONT_DSTRING_H_

+ 39 - 2
subprojects/scufeng-x86_64/include/scufeng/Mesh.h

@@ -5,6 +5,10 @@
 #include <stddef.h>
 
 #include "cglm/types.h"
+#include "scufcont/DString.h"
+#include "scufeng/engine.h"
+
+#define SEMesh_string_c_str(mesh_ptr) SCDString_c_str(&mesh_ptr->string.text)
 
 typedef enum {
     SE_TRIANGLES,
@@ -27,7 +31,8 @@ typedef struct {
 
 typedef enum {
     SE_MESH_COLOR,
-    SE_MESH_TEXTURE
+    SE_MESH_TEXTURE,
+    SE_MESH_STRING
 } SEMeshType;
 
 typedef struct {
@@ -36,6 +41,12 @@ typedef struct {
     float h;
 } SEMeshTexture;
 
+typedef struct {
+    SEMeshTexture texture;
+    SCDString text;
+    vec3 color;
+} SEMeshString;
+
 typedef struct {
     unsigned int vao;
     unsigned int vbo;
@@ -46,6 +57,7 @@ typedef struct {
 
     SEMeshType type;
     union {
+        SEMeshString string;
         SEMeshTexture texture;
         vec4 color;
     };
@@ -65,6 +77,12 @@ void SEMesh_scale_set_to_texture_size(SEMesh *const mesh);
 */
 void SEMesh_rect_textured_scale_set(SEMesh *const mesh, float x, float y);
 
+void SEMesh_textured_replace(
+    SEMesh *const mesh,
+    const unsigned int id,
+    const float w,
+    const float h
+);
 void SEMesh_line_segment_coords_set(SEMesh *const mesh, const vec2 a, const vec2 b);
 SEMesh SEMesh_line_segment_color_alloc(const vec2 a, const vec2 b, const vec4 color);
 void SEMesh_bbox_render_2d(const SEMesh *const mesh);
@@ -83,10 +101,29 @@ void SEMesh_bbox_get_2d_v(const SEMesh *const mesh, vec4 bbox);
 
 bool SEMesh_rect_is_pos_inside(const SEMesh *const mesh, const float x, const float y);
 SEMesh SEMesh_rect_color_alloc(vec4 color);
-SEMesh SEMesh_string_alloc(
+
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+SEResult SEMesh_string_alloc(
+    SEMesh *const mesh,
     const char *text,
+    const size_t text_len,
     const vec3 color
 );
+
+/**
+  @return SE_OUT_OF_MEMORY
+  @return SE_SUCCESS
+*/
+SEResult SEMesh_string_text_replace(
+    SEMesh *const mesh,
+    const char *text,
+    const size_t text_len,
+    const vec3 color
+);
+
 void SEMesh_render_multiple_2d(SEMesh *const *const meshes, const size_t meshes_n);
 
 #endif // SCUFENG_SHAPE_H_

BIN
subprojects/scufeng-x86_64/libscufeng.a