First commit

This commit is contained in:
2026-09-16 11:46:41 +03:00
commit 68f4f9a532
29 changed files with 1301 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
build
Binary file not shown.
+55
View File
@@ -0,0 +1,55 @@
project(
'scufpong',
'c',
default_options: [
'force_fallback_for=scufeng'
]
)
c_args = [
'-std=c23',
'-Wall',
'-Wextra',
'-pedantic'
]
scufeng_dep = dependency(
'scufeng',
allow_fallback: true
)
game = executable(
'game',
'src/common/game.c',
'src/game/MainMenu.c',
'src/game/MultiPlayer.c',
'src/game/SinglePlayer.c',
'src/game/pong.c',
'src/game/screen.c',
include_directories: include_directories(
'src/common/'
),
c_args: [
c_args,
'-DFONT_PATH="../assets/LiberationSans-Regular.ttf"',
'-D_DEFAULT_SOURCE' # for inet_aton
],
link_args: [
'-lm'
],
dependencies: [
scufeng_dep
]
)
server = executable(
'server',
'src/server/server.c',
c_args: [
c_args,
'-D_DEFAULT_SOURCE' # for inet_aton
],
dependencies: [
scufeng_dep
]
)
+271
View File
@@ -0,0 +1,271 @@
#include "cglm/call/vec2.h"
#include "game.h"
#include "scufeng/engine.h"
#include "scufeng/util.h"
typedef enum {
BCR_PADDLE_L_GOAL,
BCR_PADDLE_R_GOAL,
} BALL_COLLISION_RESULT;
GameState GameState_new(
const float paddles_w,
const float paddles_h,
const float paddle_l_x,
const float paddle_speed,
const float ball_w,
const vec2 ball_pos,
const float ball_speed
) {
#define BALL_START_DIR { -1.0f, 0.0f }
// clang-format off
GameState state = {
.paddle_l = {
.pos = { paddle_l_x, 0.5f },
.w = paddles_w,
.h = paddles_h,
.dirv = GLM_VEC2_ZERO_INIT,
.speed = paddle_speed
},
.paddle_r = {
.pos = { 1.0f - paddle_l_x, 0.5f },
.w = paddles_w,
.h = paddles_h,
.dirv = GLM_VEC2_ZERO_INIT,
.speed = paddle_speed
},
.ball = {
// .pos = { 0.5f, 0.5f },
.pos = { ball_pos[0], ball_pos[1] },
.w = ball_w,
.h = ball_w,
.dirv = BALL_START_DIR,
.speed = ball_speed
},
.ball_start_pos = { ball_pos[0], ball_pos[1] },
.ball_start_dir = BALL_START_DIR
};
// clang-format on
return state;
}
static void paddle_collision_check(MovingObject* const paddle) {
if (paddle->pos[1] - paddle->h / 2.0f < 0.0f) {
paddle->pos[1] = paddle->h / 2.0f;
}
if (paddle->pos[1] + paddle->h / 2.0f > 1.0f) {
paddle->pos[1] = 1.0f - paddle->h / 2.0f;
}
}
static void paddle_r_move(GameState* const state) {
const float ball_y = state->ball.pos[1];
const float target_zone_range = state->paddle_r.h / 2.0f;
const float target_zone_top = state->paddle_r.pos[1] - target_zone_range;
const float target_zone_middle = state->paddle_r.pos[1];
const float target_zone_bottom = state->paddle_r.pos[1] + target_zone_range;
const bool ball_is_below = ball_y > target_zone_bottom;
const bool ball_is_above = ball_y < target_zone_top;
const bool ball_is_centered = ball_y <= target_zone_middle + target_zone_range / 2.0f
&& ball_y >= target_zone_middle - target_zone_range / 2.0f;
// printf("%d%d\n", ball_is_below, ball_is_above);
if (ball_is_below) {
state->paddle_r.dirv[1] = 1.0f;
} else if (ball_is_above) {
state->paddle_r.dirv[1] = -1.0f;
} else if (ball_is_centered) {
state->paddle_r.dirv[1] = 0.0f;
}
}
static void game_next_round(GameState* const state) {
glmc_vec2_copy(state->ball_start_pos, state->ball.pos);
glmc_vec2_copy(state->ball_start_dir, state->ball.dirv);
}
static bool 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;
} else if (new_pos[0] - wd2 >= 1.0f) {
state->score_l++;
game_next_round(state);
return true;
}
if (new_pos[1] - hd2 < 0.0f) {
new_pos[1] = hd2;
glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, -1.0f }, ball->dirv);
}
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 false;
}
static void ball_paddles_collision_check(GameState* const state, vec2 new_pos) {
static const float possible_y_directions[] = {
0.0f,
0.5f,
-0.5f
};
MovingObject* const ball = &state->ball;
const MovingObject* const paddle_l = &state->paddle_l;
const MovingObject* const paddle_r = &state->paddle_r;
const float ball_wd2 = ball->w / 2.0f;
const float ball_hd2 = ball->h / 2.0f;
const float paddle_wd2 = paddle_l->w / 2.0f;
const float paddle_hd2 = paddle_l->h / 2.0f;
vec2 paddle_side_top;
vec2 paddle_side_bottom;
vec2 ball_side_top;
vec2 ball_side_bottom;
float paddle_back_x;
vec2 ball_pos;
vec2 new_pos_adj;
paddle_side_top[0] = paddle_l->pos[0] + paddle_wd2;
paddle_side_top[1] = paddle_l->pos[1] - paddle_hd2;
paddle_side_bottom[0] = paddle_side_top[0];
paddle_side_bottom[1] = paddle_l->pos[1] + paddle_hd2;
ball_side_top[0] = ball->pos[0] - ball_wd2;
ball_side_top[1] = ball->pos[1] - ball_hd2;
ball_side_bottom[0] = ball_side_top[0];
ball_side_bottom[1] = ball->pos[1] + ball_hd2;
paddle_back_x = paddle_l->pos[0] - paddle_wd2;
glmc_vec2_copy(ball->pos, ball_pos);
glmc_vec2_copy(new_pos, new_pos_adj);
bool collision = false;
bool mirror_ip = false;
vec2 ip;
if (state->ball.dirv[0] > 0.0f) {
mirror_ip = true;
paddle_side_top[0] = 1.0f - (paddle_r->pos[0] - paddle_r->w / 2.0f);
paddle_side_top[1] = (paddle_r->pos[1] - paddle_r->h / 2.0f);
paddle_side_bottom[0] = paddle_side_top[0];
paddle_side_bottom[1] = paddle_side_top[1] + paddle_r->h;
ball_pos[0] = 1.0f - ball_pos[0];
ball_side_top[0] = ball_pos[0] - ball_wd2;
ball_side_bottom[0] = ball_side_top[0];
paddle_back_x = 1.0f - (paddle_r->pos[0] + paddle_r->w / 2.0f);
new_pos_adj[0] = 1.0f - new_pos_adj[0];
}
// SEResult res = se_render_line_segment_norm_coord(
// paddle_side_top,
// paddle_side_bottom,
// (vec4) { 0.0f, 1.0f, 0.0f, 1.0f }
// );
// if (res != SE_SUCCESS) {
// LOG_ERRORF("Failed to draw segment: %d", res);
// }
// res = se_render_line_segment_norm_coord(
// ball_side_top,
// ball_side_bottom,
// (vec4) { 1.0f, 0.0f, 0.0f, 1.0f }
// );
// if (res != SE_SUCCESS) {
// LOG_ERRORF("Failed to draw segment: %d", res);
// }
// res = se_render_line_segment_norm_coord(
// (vec2) { paddle_back_x, 0.0f },
// (vec2) { paddle_back_x, 1.0f },
// (vec4) { 0.0f, 1.0f, 0.0f, 1.0f }
// );
// if (res != SE_SUCCESS) {
// LOG_ERRORF("Failed to draw segment: %d", res);
// }
const bool inside_paddle = (ball_side_top[0] <= paddle_side_top[0] && ball_side_top[0] >= paddle_back_x)
&& ((ball_side_top[1] <= paddle_side_bottom[1] && ball_side_top[1] >= paddle_side_top[1])
|| (ball_side_bottom[1] <= paddle_side_bottom[1] && ball_side_bottom[1] >= paddle_side_top[1]));
if (inside_paddle) {
ball->dirv[0] *= -1.0f;
ball->dirv[1] = possible_y_directions[se_rand_int() % 3];
return;
};
collision = se_line_segments_intersect(
ball_pos,
new_pos_adj,
paddle_side_top,
paddle_side_bottom,
ip
);
if (collision) {
new_pos[0] = mirror_ip ? 1.0f - ip[0] : ip[0];
new_pos[1] = ip[1];
state->ball.dirv[0] *= -1.0f;
state->ball.dirv[1] = possible_y_directions[se_rand_int() % 3];
// state->ball.dirv[1] = 0.0f;
}
}
static void 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);
glmc_vec2_scale(state->ball.dirv, ball_movement_scale, ball_movement);
vec2 new_pos;
glmc_vec2_add(state->ball.pos, ball_movement, new_pos);
if (ball_bounds_collision_check(state, new_pos)) {
return;
}
ball_paddles_collision_check(state, new_pos);
glmc_vec2_copy(new_pos, state->ball.pos);
}
void GameState_move_paddle_l(GameState* const state, const SEDirection dir) {
switch (dir) {
case SE_UP:
state->paddle_l.dirv[1] = -1.0f;
break;
case SE_DOWN:
state->paddle_l.dirv[1] = 1.0f;
break;
case SE_NONE:
state->paddle_l.dirv[1] = 0.0f;
default:
break;
}
}
void GameState_update(GameState* const state, const double delta) {
// Paddles
vec2 paddle_movement;
const float paddle_movement_scale = delta * state->paddle_l.speed;
glmc_vec2_scale(state->paddle_l.dirv, paddle_movement_scale, paddle_movement);
glmc_vec2_add(state->paddle_l.pos, paddle_movement, state->paddle_l.pos);
paddle_collision_check(&state->paddle_l);
paddle_r_move(state);
glmc_vec2_scale(state->paddle_r.dirv, paddle_movement_scale, paddle_movement);
glmc_vec2_add(state->paddle_r.pos, paddle_movement, state->paddle_r.pos);
paddle_collision_check(&state->paddle_r);
// Ball
ball_move(state, delta);
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef GAME_H_
#define GAME_H_
#include <stdint.h>
#include "cglm/types.h"
#include "scufeng/engine.h"
typedef struct {
vec2 pos;
float w;
float h;
vec2 dirv;
float speed;
} MovingObject;
typedef struct {
MovingObject paddle_l;
MovingObject paddle_r;
MovingObject ball;
vec2 ball_start_pos;
vec2 ball_start_dir;
uint8_t score_l;
uint8_t score_r;
} GameState;
GameState GameState_new(
const float paddles_w,
const float paddles_h,
const float paddle_l_x,
const float paddle_speed,
const float ball_w,
const vec2 ball_pos,
const float ball_speed
);
void GameState_move_paddle_l(GameState *const state, const SEDirection dir);
void GameState_update(GameState *const state, const double delta);
#endif // GAME_H_
+61
View File
@@ -0,0 +1,61 @@
#include <stdio.h>
#include "scufeng/engine.h"
#include "scufeng/Shape.h"
#include "scufeng/Scene.h"
#include "cglm/vec3.h"
#include "MainMenu.h"
#include "screen.h"
MainMenu MainMenu_alloc(const unsigned int screen_w, const unsigned int screen_h) {
MainMenu mm;
mm.title_text = SEMesh_string_alloc("Pong", 1.0f, GLM_VEC3_ONE);
mm.title_text.transform.pos[0] = screen_w / 2.0f;
mm.title_text.transform.pos[1] = screen_h * 0.8f;
mm.sp_text = SEMesh_string_alloc("Singleplayer", 1.0f, GLM_VEC3_ONE);
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", 1.0f, GLM_VEC3_ONE);
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];
return mm;
}
static Screen MainMenu_input_handle(MainMenu *const mm) {
if (se_get_mouse_button(SE_MOUSE_BUTTON_LEFT) == SE_KEY_ACTION_PRESS) {
double x, y;
se_get_mouse_pos(&x, &y);
if (SEMesh_rect_is_pos_inside(&mm->sp_text, x, y)) {
printf("SP\n");
return PONG_SINGLEPLAYER;
}
if (SEMesh_rect_is_pos_inside(&mm->mp_text, x, y)) {
printf("MP\n");
return PONG_MULTIPLAYER;
}
}
return PONG_MAIN_MENU;
}
Screen MainMenu_tick(MainMenu *const mm) {
const Screen screen = MainMenu_input_handle(mm);
if (screen != PONG_MAIN_MENU) {
return screen;
}
SEMesh *meshes[] = {
&mm->title_text,
&mm->sp_text,
&mm->mp_text
};
se_render_meshes_2d(meshes, sizeof(meshes) / sizeof(meshes[0]));
return PONG_MAIN_MENU;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "scufeng/Shape.h"
#include "screen.h"
typedef struct {
SEMesh title_text;
SEMesh sp_text;
SEMesh mp_text;
} MainMenu;
MainMenu MainMenu_alloc(const unsigned int screen_w, const unsigned int screen_h);
Screen MainMenu_tick(MainMenu *const mm);
+63
View File
@@ -0,0 +1,63 @@
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "scufeng/log.h"
#include "MultiPlayer.h"
MultiPlayer MultiPlayer_alloc(
const char *server_host,
const uint16_t server_port
) {
MultiPlayer mp;
struct protoent *proto = getprotobyname("tcp");
mp.sock = socket(AF_INET, SOCK_STREAM, proto->p_proto);
if (mp.sock == -1) {
LOG_FATALF("socket failed: %d %s", errno, strerror(errno));
}
struct in_addr server_addr;
int res = inet_aton(server_host, &server_addr);
if (res == 0) {
LOG_FATAL("inet_aton failed");
}
struct sockaddr_in server_sockaddr = {
.sin_family = AF_INET,
.sin_addr = server_addr,
.sin_port = htons(server_port)
};
mp.conn = connect(
mp.sock,
(struct sockaddr *) &server_sockaddr,
sizeof(server_sockaddr)
);
if (mp.conn == -1) {
LOG_FATALF("connect failed: %d %s", errno, strerror(errno));
}
return mp;
}
void MultiPlayer_free(MultiPlayer *const mp) {
close(mp->conn);
mp->conn = 0;
close(mp->sock);
mp->sock = 0;
}
void MultiPlayer_tick(MultiPlayer *const mp) {
char buf[1024] = { 0 };
const size_t buf_size = sizeof(buf) / sizeof(buf[0]);
fgets(buf, buf_size, stdin);
send(mp->conn, buf, strlen(buf), 0);
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
typedef struct {
int sock;
int conn;
} MultiPlayer;
MultiPlayer MultiPlayer_alloc(
const char *server_host,
const uint16_t server_port
);
void MultiPlayer_free(MultiPlayer *const mp);
void MultiPlayer_tick(MultiPlayer *const mp);
+122
View File
@@ -0,0 +1,122 @@
#include "cglm/vec4.h"
#include "scufeng/Scene.h"
#include "SinglePlayer.h"
#include "screen.h"
SinglePlayer SinglePlayer_alloc(
unsigned int screen_w,
unsigned int screen_h,
float paddles_w,
float paddles_h,
float paddle_l_x,
float paddle_speed,
float ball_w,
vec2 ball_pos,
float ball_speed
) {
SinglePlayer sp = {
.screen_w = screen_w,
.screen_h = screen_h,
.state = GameState_new(
paddles_w,
paddles_h,
paddle_l_x,
paddle_speed,
ball_w,
ball_pos,
ball_speed
),
.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),
};
sp.paddle_l.transform.scale[0] = screen_w * paddles_w;
sp.paddle_l.transform.scale[1] = screen_h * paddles_h;
sp.paddle_l.transform.pos[0] = screen_w * paddle_l_x;
sp.paddle_l.transform.pos[1] = screen_h / 2.0f;
sp.paddle_r.transform.scale[0] = screen_w * paddles_w;
sp.paddle_r.transform.scale[1] = screen_h * paddles_h;
sp.paddle_r.transform.pos[0] = screen_w * (1.0f - paddle_l_x);
sp.paddle_r.transform.pos[1] = screen_h / 2.0f;
sp.ball.transform.scale[0] = screen_w * ball_w;
sp.ball.transform.scale[1] = sp.ball.transform.scale[0];
sp.ball.transform.pos[0] = screen_w * ball_pos[0];
sp.ball.transform.pos[1] = screen_h * ball_pos[1];
return sp;
}
void SinglePlayer_free(SinglePlayer *sp) {
SEMesh_free(&sp->paddle_l);
SEMesh_free(&sp->paddle_r);
SEMesh_free(&sp->ball);
}
static void pos_convert(
vec3 render_pos,
const vec3 state_pos,
const unsigned int screen_w,
const unsigned int screen_h
) {
render_pos[0] = state_pos[0] * screen_w;
render_pos[1] = (1.0f - state_pos[1]) * screen_h;
}
static void update_shapes(
const GameState *const state,
SEMesh *const paddle_l,
SEMesh *const paddle_r,
SEMesh *const ball,
const unsigned int screen_w,
const unsigned int screen_h
) {
// Paddles
pos_convert(paddle_l->transform.pos, state->paddle_l.pos, screen_w, screen_h);
pos_convert(paddle_r->transform.pos, state->paddle_r.pos, screen_w, screen_h);
// Ball
pos_convert(ball->transform.pos, state->ball.pos, screen_w, screen_h);
}
static bool key_input_handle(GameState *const state) {
if (se_get_key(SE_KEY_W) == SE_KEY_ACTION_PRESS) {
GameState_move_paddle_l(state, SE_UP);
} else if (se_get_key(SE_KEY_S) == SE_KEY_ACTION_PRESS) {
GameState_move_paddle_l(state, SE_DOWN);
} else {
GameState_move_paddle_l(state, SE_NONE);
}
if (se_get_key(SE_KEY_ESCAPE) == SE_KEY_ACTION_PRESS) {
return true;
}
return false;
}
Screen SinglePlayer_tick(SinglePlayer *const sp, const double delta) {
if (key_input_handle(&sp->state)) {
return PONG_MAIN_MENU;
}
GameState_update(&sp->state, delta);
update_shapes(
&sp->state,
&sp->paddle_l,
&sp->paddle_r,
&sp->ball,
sp->screen_w,
sp->screen_h
);
SEMesh *meshes[] = {
&sp->paddle_l,
&sp->paddle_r,
&sp->ball
};
se_render_meshes_2d(meshes, sizeof(meshes) / sizeof(meshes[0]));
return PONG_SINGLEPLAYER;
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "scufeng/Shape.h"
#include "game.h"
#include "screen.h"
typedef struct {
unsigned int screen_w;
unsigned int screen_h;
GameState state;
SEMesh paddle_l;
SEMesh paddle_r;
SEMesh ball;
} SinglePlayer;
SinglePlayer SinglePlayer_alloc(
unsigned int screen_w,
unsigned int screen_h,
float paddles_w,
float paddles_h,
float paddle_l_x,
float paddle_speed,
float ball_w,
vec2 ball_pos,
float ball_speed
);
void SinglePlayer_free(SinglePlayer *sp);
Screen SinglePlayer_tick(SinglePlayer *const sp, const double delta);
+78
View File
@@ -0,0 +1,78 @@
#include "cglm/types.h"
#include "scufeng/engine.h"
#include "scufeng/text.h"
#include "screen.h"
#include "MainMenu.h"
#include "SinglePlayer.h"
#include "MultiPlayer.h"
static const unsigned int screen_w = 1200;
static const unsigned int screen_h = 600;
static const float paddles_w = 0.01f;
static const float paddles_h = 0.10f;
static const float paddle_l_x = (1.0f / 16);
static const float ball_w = paddles_w * 0.5f;
static vec2 ball_pos = { 0.5f, 0.5f };
static const float paddle_speed = 0.7f;
static const float ball_speed = 0.7f;
int main() {
se_init(true);
se_window_create(screen_w, screen_h, "Pong", -1);
se_swap_interval(0);
se_fps_lock_set_fps(60);
se_font_load(FONT_PATH, 0, 48);
se_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
MainMenu mm = MainMenu_alloc(screen_w, screen_h);
SinglePlayer sp;
double last_frame_time = 0.0;
while (!se_window_should_close()) {
const double current_time = se_get_time();
const double delta = current_time - last_frame_time;
last_frame_time = current_time;
se_fps_lock_frame_start_s(current_time);
se_clear();
se_handle_events();
switch (current_screen) {
case PONG_MAIN_MENU:
current_screen = MainMenu_tick(&mm);
if (current_screen == PONG_SINGLEPLAYER) {
sp = SinglePlayer_alloc(
screen_w,
screen_h,
paddles_w,
paddles_h,
paddle_l_x,
paddle_speed,
ball_w,
ball_pos,
ball_speed
);
}
break;
case PONG_SINGLEPLAYER:
current_screen = SinglePlayer_tick(&sp, delta);
if (current_screen == PONG_MAIN_MENU) {
SinglePlayer_free(&sp);
}
break;
case PONG_MULTIPLAYER:
MultiPlayer_tick();
break;
}
// printf("%u %u\n", sp.state.score_l, sp.state.score_r);
se_fps_lock_wait();
se_swap_buffers();
// printf("FT: %lf FPS: %lf\n", se_get_time() - current_time, 1.0 / (se_get_time() - current_time));
}
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
#include "screen.h"
Screen current_screen = PONG_MAIN_MENU;
+9
View File
@@ -0,0 +1,9 @@
#pragma once
typedef enum {
PONG_MAIN_MENU,
PONG_SINGLEPLAYER,
PONG_MULTIPLAYER,
} Screen;
extern Screen current_screen;
+70
View File
@@ -0,0 +1,70 @@
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <arpa/inet.h>
#include "scufeng/log.h"
#define SERVER_HOST "127.0.0.1"
#define SERVER_PORT 8080
int main() {
struct protoent *proto = getprotobyname("tcp");
if (proto == nullptr) {
LOG_FATAL("getprotobyname failed");
}
const int sock = socket(AF_INET, SOCK_STREAM, proto->p_proto);
if (sock == -1) {
LOG_FATALF("socket failed: %s", strerror(errno));
}
{
struct in_addr addr;
int res = inet_aton(SERVER_HOST, &addr);
if (res == 0) {
LOG_FATAL("inet_aton failed");
}
struct sockaddr_in own_addr = {
.sin_family = AF_INET,
.sin_addr = addr,
.sin_port = htons(SERVER_PORT)
};
res = bind(sock, (struct sockaddr*) &own_addr, sizeof(own_addr));
if (res == -1) {
LOG_FATALF("bind failed: %s", strerror(errno));
}
}
int res = listen(sock, 5);
if (res == -1) {
LOG_FATALF("listen failed: %s", strerror(errno));
}
int conn = -1;
conn = accept(sock, nullptr, nullptr);
if (conn == -1) {
LOG_FATALF("accept failed: %s", strerror(errno));
}
char buf[1024] = {0};
const size_t buf_size = sizeof(buf) / sizeof(buf[0]);
while (true) {
int received = recv(conn, buf, buf_size, 0);
if (received == 0) {
printf("buf: %s\n", buf);
buf[0] = '\0';
break;
} else if (received == -1) {
LOG_FATALF("recv failed: %d %s", errno, strerror(errno));
} else {
buf[received] = '\0';
}
}
return 0;
}
View File
+1
View File
@@ -0,0 +1 @@
c505e04113e221188440051de27fad12c0670205
@@ -0,0 +1,37 @@
#ifndef SCUFENG_CAMERA_H_
#define SCUFENG_CAMERA_H_
#include "cglm/types.h"
#include "scufeng/engine.h"
typedef struct {
vec3 pos;
float pitch_rad;
float yaw_rad;
float fov_rad;
float near_clip;
float far_clip;
vec3 front;
vec3 right;
mat4 view;
mat4 projection;
} SECamera;
SECamera SECamera_new(
vec3 pos,
const float fov_rad,
const float near_clip,
const float far_clip
);
void SECamera_set_perspective(SECamera *const camera);
void SECamera_pos_set(SECamera *const camera, vec3 pos);
void SECamera_move_to_direction(SECamera *const camera, SEDirection dir, const float mag);
void SECamera_look_dir(SECamera *const camera, vec3 dir);
void SECamera_look_at(SECamera *const camera, vec3 target);
void SECamera_pitch_add(SECamera *const camera, const float pitch_rad);
void SECamera_yaw_add(SECamera *const camera, const float yaw_rad);
void SECamera_view_update(SECamera *const camera);
#endif // SCUFENG_CAMERA_H_
@@ -0,0 +1,12 @@
#ifndef SCUFENG_SCENE_H_
#define SCUFENG_SCENE_H_
#include "scufeng/Shape.h"
#include "engine.h"
void se_render_meshes_2d(SEMesh *const *const meshes, const size_t meshes_n);
SEResult se_render_line_segment(const vec2 p1, const vec2 p2, const vec4 color);
SEResult se_render_line_segment_norm_coord(const vec2 p1, const vec2 p2, const vec4 color);
void se_text_render(const char* text, int x, int y, float scale, const vec3 color);
#endif // SCUFENG_SCENE_H_
@@ -0,0 +1,90 @@
#ifndef SCUFENG_SHAPE_H_
#define SCUFENG_SHAPE_H_
#include <stddef.h>
#include <stdbool.h>
#include "cglm/types.h"
#include "scufeng/engine.h"
typedef enum {
SE_TRIANGLES,
SE_TRIANGLE_STRIP,
SE_TRIANGLE_FAN,
SE_LINES,
SE_LINE_STRIP
} SEDrawMode;
typedef struct {
vec3 pos;
vec3 scale;
vec3 rotation;
} SETransform;
typedef struct {
float pos[3];
float tex_coords[2];
} SEVertex;
typedef enum {
SE_MESH_COLOR,
SE_MESH_TEXTURE
} SEMeshType;
typedef struct {
unsigned int id;
float w;
float h;
} SEMeshTexture;
typedef struct {
unsigned int vao;
unsigned int vbo;
size_t verts_n;
int draw_mode;
SETransform transform;
SEMeshType type;
union {
SEMeshTexture texture;
vec4 color;
};
} SEMesh;
SEResult SEShape_alloc(
SETransform *const shape,
const SEVertex *const vertices,
const size_t vertices_n,
const vec4 color,
const SEDrawMode draw_mode
);
void SEMesh_free(SEMesh *const mesh);
void SEMesh_set_draw_mode(SEMesh *mesh, const SEDrawMode mode);
void SEMesh_render(const SEMesh *const mesh);
void SEMesh_bbox_get_2d(
const SEMesh *const mesh,
float *left,
float *right,
float *top,
float *bottom
);
bool SEMesh_rect_is_pos_inside(const SEMesh *const mesh, const float x, const float y);
void SEShape_rotation_set(SETransform *shape, vec3 rotation);
void SEShape_rotate_2d_center(SETransform *const shape, const float w, const float h, const float angle_rad);
void SEShape_scale_set(SETransform *shape, vec3 scale);
void SEShape_scale(SETransform *const shape, vec3 scale);
void SEShape_pos_set(SETransform *const shape, vec3 pos);
void SEShape_translate_2d_center(SETransform *const shape, const float w, const float h, const vec2 pos);
void SEShape_transform_reset(SETransform *shape);
SEMesh SEMesh_rect_color_alloc(vec4 color);
SEMesh SEMesh_string_alloc(
const char *text,
const float scale,
const vec3 color
);
// void SEShape_transform_apply(SEShape *const shape);
#endif // SCUFENG_SHAPE_H_
@@ -0,0 +1,74 @@
#ifndef SCUFENG_H_
#define SCUFENG_H_
#include <stdbool.h>
#include "keys.h" // IWYU pragma: export
#define SERESULT_X(X) \
X(SE_SUCCESS) \
X(SE_OUT_OF_MEMORY) \
X(SE_FC_FAILURE) \
X(SE_NOT_FOUND) \
X(SE_FT_FAILURE)
#define X(val) val,
typedef enum {
SERESULT_X(X)
} SEResult;
#undef X
typedef enum {
SE_FILL,
SE_LINE,
SE_POINT
} SEPolygonMode;
typedef enum {
SE_NONE,
SE_FORWARD,
SE_BACKWARD,
SE_LEFT,
SE_RIGHT,
SE_UP,
SE_DOWN
} SEDirection;
typedef void (*SEKeyCb)(SEKey key, SEKeyAction action, SEKeyMod mods);
void se_init(const bool debug);
void se_deinit(void);
void se_window_create(
const int w,
const int h,
const char* title,
const int fullscreen_monitor_i
);
void se_window_close();
void se_clear_color(
const float r,
const float g,
const float b,
const float a
);
void se_clear(void);
void se_swap_buffers(void);
bool se_window_should_close(void);
void se_handle_events(void);
double se_get_time(void);
double se_delta_time(const double current_time);
void se_set_polygon_mode(const SEPolygonMode mode);
void se_set_key_callback(SEKeyCb cb);
SEKeyAction se_get_key(const SEKey key);
SEKeyAction se_get_mouse_button(const SEMouseButton btn);
void se_get_mouse_pos(double *x, double *y);
void se_swap_interval(const int interval);
void se_fps_lock_set_fps(const unsigned int fps);
void se_fps_lock_frame_start_s(const double frame_start_s);
void se_fps_lock_wait(void);
const char* SEResult_to_str(const SEResult res);
#endif // SCUFENG_H_
+164
View File
@@ -0,0 +1,164 @@
#ifndef SCUFENG_KEYS_H_
#define SCUFENG_KEYS_H_
#include "GLFW/glfw3.h"
typedef enum {
SE_KEY_UNKNOWN = GLFW_KEY_UNKNOWN,
/* Printable keys */
SE_KEY_SPACE = GLFW_KEY_SPACE,
SE_KEY_APOSTROPHE = GLFW_KEY_APOSTROPHE,
SE_KEY_COMMA = GLFW_KEY_COMMA,
SE_KEY_MINUS = GLFW_KEY_MINUS,
SE_KEY_PERIOD = GLFW_KEY_PERIOD,
SE_KEY_SLASH = GLFW_KEY_SLASH,
SE_KEY_0 = GLFW_KEY_0,
SE_KEY_1 = GLFW_KEY_1,
SE_KEY_2 = GLFW_KEY_2,
SE_KEY_3 = GLFW_KEY_3,
SE_KEY_4 = GLFW_KEY_4,
SE_KEY_5 = GLFW_KEY_5,
SE_KEY_6 = GLFW_KEY_6,
SE_KEY_7 = GLFW_KEY_7,
SE_KEY_8 = GLFW_KEY_8,
SE_KEY_9 = GLFW_KEY_9,
SE_KEY_SEMICOLON = GLFW_KEY_SEMICOLON,
SE_KEY_EQUAL = GLFW_KEY_EQUAL,
SE_KEY_A = GLFW_KEY_A,
SE_KEY_B = GLFW_KEY_B,
SE_KEY_C = GLFW_KEY_C,
SE_KEY_D = GLFW_KEY_D,
SE_KEY_E = GLFW_KEY_E,
SE_KEY_F = GLFW_KEY_F,
SE_KEY_G = GLFW_KEY_G,
SE_KEY_H = GLFW_KEY_H,
SE_KEY_I = GLFW_KEY_I,
SE_KEY_J = GLFW_KEY_J,
SE_KEY_K = GLFW_KEY_K,
SE_KEY_L = GLFW_KEY_L,
SE_KEY_M = GLFW_KEY_M,
SE_KEY_N = GLFW_KEY_N,
SE_KEY_O = GLFW_KEY_O,
SE_KEY_P = GLFW_KEY_P,
SE_KEY_Q = GLFW_KEY_Q,
SE_KEY_R = GLFW_KEY_R,
SE_KEY_S = GLFW_KEY_S,
SE_KEY_T = GLFW_KEY_T,
SE_KEY_U = GLFW_KEY_U,
SE_KEY_V = GLFW_KEY_V,
SE_KEY_W = GLFW_KEY_W,
SE_KEY_X = GLFW_KEY_X,
SE_KEY_Y = GLFW_KEY_Y,
SE_KEY_Z = GLFW_KEY_Z,
SE_KEY_LEFT_BRACKET = GLFW_KEY_LEFT_BRACKET,
SE_KEY_BACKSLASH = GLFW_KEY_BACKSLASH,
SE_KEY_RIGHT_BRACKET = GLFW_KEY_RIGHT_BRACKET,
SE_KEY_GRAVE_ACCENT = GLFW_KEY_GRAVE_ACCENT,
SE_KEY_WORLD_1 = GLFW_KEY_WORLD_1,
SE_KEY_WORLD_2 = GLFW_KEY_WORLD_2,
/* Function keys */
SE_KEY_ESCAPE = GLFW_KEY_ESCAPE,
SE_KEY_ENTER = GLFW_KEY_ENTER,
SE_KEY_TAB = GLFW_KEY_TAB,
SE_KEY_BACKSPACE = GLFW_KEY_BACKSPACE,
SE_KEY_INSERT = GLFW_KEY_INSERT,
SE_KEY_DELETE = GLFW_KEY_DELETE,
SE_KEY_RIGHT = GLFW_KEY_RIGHT,
SE_KEY_LEFT = GLFW_KEY_LEFT,
SE_KEY_DOWN = GLFW_KEY_DOWN,
SE_KEY_UP = GLFW_KEY_UP,
SE_KEY_PAGE_UP = GLFW_KEY_PAGE_UP,
SE_KEY_PAGE_DOWN = GLFW_KEY_PAGE_DOWN,
SE_KEY_HOME = GLFW_KEY_HOME,
SE_KEY_END = GLFW_KEY_END,
SE_KEY_CAPS_LOCK = GLFW_KEY_CAPS_LOCK,
SE_KEY_SCROLL_LOCK = GLFW_KEY_SCROLL_LOCK,
SE_KEY_NUM_LOCK = GLFW_KEY_NUM_LOCK,
SE_KEY_PRINT_SCREEN = GLFW_KEY_PRINT_SCREEN,
SE_KEY_PAUSE = GLFW_KEY_PAUSE,
SE_KEY_F1 = GLFW_KEY_F1,
SE_KEY_F2 = GLFW_KEY_F2,
SE_KEY_F3 = GLFW_KEY_F3,
SE_KEY_F4 = GLFW_KEY_F4,
SE_KEY_F5 = GLFW_KEY_F5,
SE_KEY_F6 = GLFW_KEY_F6,
SE_KEY_F7 = GLFW_KEY_F7,
SE_KEY_F8 = GLFW_KEY_F8,
SE_KEY_F9 = GLFW_KEY_F9,
SE_KEY_F10 = GLFW_KEY_F10,
SE_KEY_F11 = GLFW_KEY_F11,
SE_KEY_F12 = GLFW_KEY_F12,
SE_KEY_F13 = GLFW_KEY_F13,
SE_KEY_F14 = GLFW_KEY_F14,
SE_KEY_F15 = GLFW_KEY_F15,
SE_KEY_F16 = GLFW_KEY_F16,
SE_KEY_F17 = GLFW_KEY_F17,
SE_KEY_F18 = GLFW_KEY_F18,
SE_KEY_F19 = GLFW_KEY_F19,
SE_KEY_F20 = GLFW_KEY_F20,
SE_KEY_F21 = GLFW_KEY_F21,
SE_KEY_F22 = GLFW_KEY_F22,
SE_KEY_F23 = GLFW_KEY_F23,
SE_KEY_F24 = GLFW_KEY_F24,
SE_KEY_F25 = GLFW_KEY_F25,
SE_KEY_KP_0 = GLFW_KEY_KP_0,
SE_KEY_KP_1 = GLFW_KEY_KP_1,
SE_KEY_KP_2 = GLFW_KEY_KP_2,
SE_KEY_KP_3 = GLFW_KEY_KP_3,
SE_KEY_KP_4 = GLFW_KEY_KP_4,
SE_KEY_KP_5 = GLFW_KEY_KP_5,
SE_KEY_KP_6 = GLFW_KEY_KP_6,
SE_KEY_KP_7 = GLFW_KEY_KP_7,
SE_KEY_KP_8 = GLFW_KEY_KP_8,
SE_KEY_KP_9 = GLFW_KEY_KP_9,
SE_KEY_KP_DECIMAL = GLFW_KEY_KP_DECIMAL,
SE_KEY_KP_DIVIDE = GLFW_KEY_KP_DIVIDE,
SE_KEY_KP_MULTIPLY = GLFW_KEY_KP_MULTIPLY,
SE_KEY_KP_SUBTRACT = GLFW_KEY_KP_SUBTRACT,
SE_KEY_KP_ADD = GLFW_KEY_KP_ADD,
SE_KEY_KP_ENTER = GLFW_KEY_KP_ENTER,
SE_KEY_KP_EQUAL = GLFW_KEY_KP_EQUAL,
SE_KEY_LEFT_SHIFT = GLFW_KEY_LEFT_SHIFT,
SE_KEY_LEFT_CONTROL = GLFW_KEY_LEFT_CONTROL,
SE_KEY_LEFT_ALT = GLFW_KEY_LEFT_ALT,
SE_KEY_LEFT_SUPER = GLFW_KEY_LEFT_SUPER,
SE_KEY_RIGHT_SHIFT = GLFW_KEY_RIGHT_SHIFT,
SE_KEY_RIGHT_CONTROL = GLFW_KEY_RIGHT_CONTROL,
SE_KEY_RIGHT_ALT = GLFW_KEY_RIGHT_ALT,
SE_KEY_RIGHT_SUPER = GLFW_KEY_RIGHT_SUPER,
SE_KEY_MENU = GLFW_KEY_MENU,
} SEKey;
typedef enum {
SE_KEY_ACTION_RELEASE = GLFW_RELEASE,
SE_KEY_ACTION_PRESS = GLFW_PRESS,
SE_KEY_ACTION_REPEAT = GLFW_REPEAT,
} SEKeyAction;
typedef enum {
SE_KEY_MOD_SHIFT = GLFW_MOD_SHIFT,
SE_KEY_MOD_CONTROL = GLFW_MOD_CONTROL,
SE_KEY_MOD_ALT = GLFW_MOD_ALT,
SE_KEY_MOD_SUPER = GLFW_MOD_SUPER,
SE_KEY_MOD_CAPS_LOCK = GLFW_MOD_CAPS_LOCK,
SE_KEY_MOD_NUM_LOCK = GLFW_MOD_NUM_LOCK,
} SEKeyMod;
typedef enum {
SE_MOUSE_BUTTON_1 = 0,
SE_MOUSE_BUTTON_2 = 1,
SE_MOUSE_BUTTON_3 = 2,
SE_MOUSE_BUTTON_4 = 3,
SE_MOUSE_BUTTON_5 = 4,
SE_MOUSE_BUTTON_6 = 5,
SE_MOUSE_BUTTON_7 = 6,
SE_MOUSE_BUTTON_8 = 7,
SE_MOUSE_BUTTON_LAST = GLFW_MOUSE_BUTTON_8,
SE_MOUSE_BUTTON_LEFT = GLFW_MOUSE_BUTTON_1,
SE_MOUSE_BUTTON_RIGHT = GLFW_MOUSE_BUTTON_2,
SE_MOUSE_BUTTON_MIDDLE = GLFW_MOUSE_BUTTON_3,
} SEMouseButton;
#endif // SCUFENG_KEYS_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef LOG_H_
#define LOG_H_
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#define LOG(str) log_with_tag(stdout, __FILE__, __LINE__, "LOG", (str))
#define LOGF(fmt, ...) logf_with_tag(stdout, __FILE__, __LINE__, "LOG", (fmt), __VA_ARGS__)
#define LOG_ERROR(str) log_with_tag(stderr, __FILE__, __LINE__, "ERROR", (str))
#define LOG_ERRORF(fmt, ...) logf_with_tag(stderr, __FILE__, __LINE__, "ERROR", (fmt), __VA_ARGS__)
#define LOG_FATAL(str) LOG_ERROR(str); exit(-1)
#define LOG_FATALF(fmt, ...) LOG_ERRORF(fmt, __VA_ARGS__); exit(-1)
#define LOG_FATAL_TODO(str) log_with_tag(stderr, __FILE__, __LINE__, "FATAL TODO", (str)); exit(-1)
#define LOG_FATAL_TODOF(fmt, ...) logf_with_tag(stderr, __FILE__, __LINE__, "FATAL TODO", (fmt), __VA_ARGS__); exit(-1)
void log_with_tag(FILE* out_file, char* file_name, size_t line, const char* tag, char* str);
void logf_with_tag(FILE* out_file, char* file_name, size_t line, const char* tag, char* fmt, ...);
#endif // LOG_H_
@@ -0,0 +1,18 @@
#ifndef SCUFENG_MODEL_H_
#define SCUFENG_MODEL_H_
#include <stddef.h>
typedef struct {
float* vertices;
size_t vertices_n;
} Mesh;
typedef struct {
Mesh* meshes;
size_t meshes_n;
} Model;
Model model_load_gltf(const char* path);
#endif // SCUFENG_MODEL_H_
@@ -0,0 +1,9 @@
#ifndef SE_SCUFCONT_IMPL_H_
#define SE_SCUFCONT_IMPL_H_
#include "scufcont/DArray.h"
#include "scufeng/Shape.h"
SCDARRAY_HEADER(SETransform*, SEShapep)
#endif // SE_SCUFCONT_IMPL_H_
@@ -0,0 +1,12 @@
#ifndef SCUFENG_TEXT_H_
#define SCUFENG_TEXT_H_
#include "scufeng/engine.h"
SEResult se_font_load(
const char *path,
const unsigned int pixel_w,
const unsigned int pixel_h
);
#endif // SCUFENG_TEXT_H_
@@ -0,0 +1,15 @@
#ifndef SCUFENG_UTIL_H_
#define SCUFENG_UTIL_H_
#include "cglm/types.h"
int se_rand_int();
bool se_line_segments_intersect(
const vec2 a1,
const vec2 a2,
const vec2 b1,
const vec2 b2,
vec2 ip
);
#endif // SCUFENG_UTIL_H_
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
project('scufeng', 'c')
cc = meson.get_compiler('c')
scufeng_lib_dep = cc.find_library(
'scufeng',
dirs: meson.current_source_dir()
)
scufeng_dep = declare_dependency(
dependencies: scufeng_lib_dep,
include_directories: include_directories('include')
)
meson.override_dependency('scufeng', scufeng_dep)