From a15cb041654ae307add0b998b526c87c3f42bf5f Mon Sep 17 00:00:00 2001 From: David Moc Date: Tue, 2 Jun 2026 13:50:21 +0200 Subject: Add plugin hooks and mode plugins --- config/tetris.c | 188 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 78 deletions(-) (limited to 'config/tetris.c') diff --git a/config/tetris.c b/config/tetris.c index 542963a..e86ab75 100644 --- a/config/tetris.c +++ b/config/tetris.c @@ -9,8 +9,7 @@ #define TETRIS_VAR_NEXT "next_piece" typedef struct tetris_state { - ecex_t *ed; - int *board; + ecex_plugin_t *plugin; int piece; int rot; int x; @@ -34,28 +33,61 @@ static int tetris_idx(int x, int y) { } static int tetris_shape_cell(int piece, int rot, int col, int row) { - return ecex_tetris_shape_cell(piece, rot, col, row); + int p = piece % 7; + int r = rot & 3; + if (p < 0) p += 7; + if (col < 0 || col >= 4 || row < 0 || row >= 4) return 0; + + if (p == 0) { + if ((r & 1) == 0) return row == 1; + return col == 1; + } + if (p == 1) return (row == 1 || row == 2) && (col == 1 || col == 2); + if (p == 2) { + if (r == 0) return (row == 1 && col >= 0 && col <= 2) || (row == 2 && col == 1); + if (r == 1) return (col == 1 && row >= 0 && row <= 2) || (row == 1 && col == 2); + if (r == 2) return (row == 1 && col >= 0 && col <= 2) || (row == 0 && col == 1); + return (col == 1 && row >= 0 && row <= 2) || (row == 1 && col == 0); + } + if (p == 3) { + if ((r & 1) == 0) return (row == 1 && (col == 1 || col == 2)) || (row == 2 && (col == 0 || col == 1)); + return (col == 1 && (row == 0 || row == 1)) || (col == 2 && (row == 1 || row == 2)); + } + if (p == 4) { + if ((r & 1) == 0) return (row == 1 && (col == 0 || col == 1)) || (row == 2 && (col == 1 || col == 2)); + return (col == 2 && (row == 0 || row == 1)) || (col == 1 && (row == 1 || row == 2)); + } + if (p == 5) { + if (r == 0) return (row == 1 && col >= 0 && col <= 2) || (row == 0 && col == 0); + if (r == 1) return (col == 1 && row >= 0 && row <= 2) || (row == 0 && col == 2); + if (r == 2) return (row == 1 && col >= 0 && col <= 2) || (row == 2 && col == 2); + return (col == 1 && row >= 0 && row <= 2) || (row == 2 && col == 0); + } + if (r == 0) return (row == 1 && col >= 0 && col <= 2) || (row == 0 && col == 2); + if (r == 1) return (col == 1 && row >= 0 && row <= 2) || (row == 2 && col == 2); + if (r == 2) return (row == 1 && col >= 0 && col <= 2) || (row == 2 && col == 0); + return (col == 1 && row >= 0 && row <= 2) || (row == 0 && col == 0); } -static int *tetris_board(ecex_t *ed, tetris_state_t *s) { - if (!ed || !s) return 0; - return (int *)ecex_var_get_or_alloc(ed, s, TETRIS_VAR_BOARD, (size_t)TETRIS_CELLS, sizeof(int)); +static int *tetris_board(tetris_state_t *s) { + if (!s) return 0; + return (int *)ecex_plugin_slot_alloc(s->plugin, TETRIS_VAR_BOARD, (size_t)TETRIS_CELLS, sizeof(int)); } -static int tetris_next_piece(ecex_t *ed, tetris_state_t *s) { - return ecex_var_i32(ed, s, TETRIS_VAR_NEXT, 0); +static int tetris_next_piece(tetris_state_t *s) { + return ecex_plugin_slot_i32_get_scalar(s ? s->plugin : 0, TETRIS_VAR_NEXT, 0); } -static void tetris_set_next_piece(ecex_t *ed, tetris_state_t *s, int piece) { +static void tetris_set_next_piece(tetris_state_t *s, int piece) { if (piece < 0) piece = 0; if (piece > 6) piece = piece % 7; - ecex_var_i32_set_scalar(ed, s, TETRIS_VAR_NEXT, piece); + ecex_plugin_slot_i32_set_scalar(s ? s->plugin : 0, TETRIS_VAR_NEXT, piece); } -static void tetris_clear_board(ecex_t *ed, tetris_state_t *s) { +static void tetris_clear_board(tetris_state_t *s) { int *board; if (!s) { ecex_log("tetris_clear_board: null state"); return; } - board = tetris_board(ed, s); + board = tetris_board(s); ecex_log_ptr("tetris_clear_board: board=", board); ecex_mem_zero(board, (size_t)TETRIS_CELLS * sizeof(int)); ecex_log("tetris_clear_board: done"); @@ -71,11 +103,11 @@ static int tetris_pick(tetris_state_t *s) { return piece; } -static int tetris_collides(ecex_t *ed, tetris_state_t *s, int piece, int rot, int px, int py) { +static int tetris_collides(tetris_state_t *s, int piece, int rot, int px, int py) { int r; int c; - int *board = tetris_board(ed, s); + int *board = tetris_board(s); if (!s || !board) return 1; for (r = 0; r < 4; ++r) { for (c = 0; c < 4; ++c) { @@ -93,44 +125,44 @@ static int tetris_collides(ecex_t *ed, tetris_state_t *s, int piece, int rot, in return 0; } -static void tetris_spawn(ecex_t *ed, tetris_state_t *s) { +static void tetris_spawn(tetris_state_t *s) { int queued; ecex_log_ptr("tetris_spawn: state=", s); if (!s) return; /* The sidebar must show the piece that will become active on the next - * spawn. Keep that queued value in the host variable registry, then consume + * spawn. Keep that queued value in a plugin slot, then consume * it here and immediately replace it with a freshly-picked preview. */ - queued = tetris_next_piece(ed, s); + queued = tetris_next_piece(s); if (queued < 0 || queued > 6) queued = tetris_pick(s); s->piece = queued; - tetris_set_next_piece(ed, s, tetris_pick(s)); + tetris_set_next_piece(s, tetris_pick(s)); ecex_log_int("tetris_spawn: current_piece=", s->piece); - ecex_log_int("tetris_spawn: preview_piece=", tetris_next_piece(ed, s)); + ecex_log_int("tetris_spawn: preview_piece=", tetris_next_piece(s)); s->rot = 0; s->x = 3; s->y = -1; s->last_drop_ms = 0; - if (tetris_collides(ed, s, s->piece, s->rot, s->x, s->y)) { + if (tetris_collides(s, s->piece, s->rot, s->x, s->y)) { ecex_log("tetris_spawn: immediate collision -> game over"); s->game_over = 1; } } -static void tetris_reset(ecex_t *ed, tetris_state_t *s) { +static void tetris_reset(tetris_state_t *s) { ecex_log_ptr("tetris_reset: state=", s); if (!s) return; - tetris_clear_board(ed, s); + tetris_clear_board(s); ecex_log("tetris_reset: after clear"); /* Avoid calling libc/time-returning host functions from CCDJIT plugin code. * Seed from stable host-owned addresses instead; good enough for a toy game * and much safer for the tiny JIT ABI. */ - s->rng = 0x9e3779b9u ^ (unsigned int)(size_t)s ^ (unsigned int)(size_t)tetris_board(ed, s); + s->rng = 0x9e3779b9u ^ (unsigned int)(size_t)s ^ (unsigned int)(size_t)tetris_board(s); ecex_log_int("tetris_reset: rng=", (int)s->rng); s->score = 0; s->lines = 0; @@ -139,15 +171,15 @@ static void tetris_reset(ecex_t *ed, tetris_state_t *s) { s->paused = 0; s->last_drop_ms = 0; s->drop_interval_ms = 650; - tetris_set_next_piece(ed, s, tetris_pick(s)); + tetris_set_next_piece(s, tetris_pick(s)); s->log_tick_count = 0; s->log_draw_count = 0; ecex_log("tetris_reset: spawning first piece"); - tetris_spawn(ed, s); + tetris_spawn(s); ecex_log("tetris_reset: done"); } -static void tetris_lock(ecex_t *ed, tetris_state_t *s) { +static void tetris_lock(tetris_state_t *s) { ecex_log_ptr("tetris_lock: state=", s); int r; int c; @@ -156,7 +188,7 @@ static void tetris_lock(ecex_t *ed, tetris_state_t *s) { int *board; if (!s) return; - board = tetris_board(ed, s); + board = tetris_board(s); if (!board) return; for (r = 0; r < 4; ++r) { @@ -214,7 +246,7 @@ static void tetris_lock(ecex_t *ed, tetris_state_t *s) { } ecex_log_int("tetris_lock: cleared=", cleared); - tetris_spawn(ed, s); + tetris_spawn(s); } @@ -244,11 +276,11 @@ static int tetris_top_out_if_hidden(tetris_state_t *s, const char *where) { return 1; } -static int tetris_soft_drop(ecex_t *ed, tetris_state_t *s) { +static int tetris_soft_drop(tetris_state_t *s) { if (!s) { ecex_log("tetris_soft_drop: null state"); return 0; } if (s->game_over || s->paused) return 0; - if (!tetris_collides(ed, s, s->piece, s->rot, s->x, s->y + 1)) { + if (!tetris_collides(s, s->piece, s->rot, s->x, s->y + 1)) { ++s->y; return 1; } @@ -256,12 +288,12 @@ static int tetris_soft_drop(ecex_t *ed, tetris_state_t *s) { ecex_log_int("tetris_soft_drop: locking piece=", s->piece); ecex_log_int("tetris_soft_drop: y=", s->y); if (tetris_top_out_if_hidden(s, "tetris_soft_drop: hidden lock -> game over")) return 1; - tetris_lock(ed, s); + tetris_lock(s); return 0; } -static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) { +static int tetris_move_horizontal(tetris_state_t *s, int dx) { int old_x; int old_y; int old_rot; @@ -273,7 +305,7 @@ static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) { old_y = s->y; old_rot = s->rot; - if (!tetris_collides(ed, s, s->piece, s->rot, old_x + dx, old_y)) { + if (!tetris_collides(s, s->piece, s->rot, old_x + dx, old_y)) { s->x = old_x + dx; /* Horizontal movement must never vertical-kick the active piece. * Keep these assignments explicit because plugin/JIT callback bugs are @@ -296,18 +328,18 @@ static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) { return 0; } -static void tetris_hard_drop(ecex_t *ed, tetris_state_t *s) { +static void tetris_hard_drop(tetris_state_t *s) { int moved = 0; if (!s || s->game_over || s->paused) return; - while (!tetris_collides(ed, s, s->piece, s->rot, s->x, s->y + 1)) { + while (!tetris_collides(s, s->piece, s->rot, s->x, s->y + 1)) { ++s->y; ++moved; } s->score += moved * 2; if (tetris_top_out_if_hidden(s, "tetris_hard_drop: hidden lock -> game over")) return; - tetris_lock(ed, s); + tetris_lock(s); } static tetris_state_t *tetris_state_for_ed(ecex_t *ed) { @@ -330,14 +362,14 @@ static int tetris_alpha8(int alpha) { static void tetris_color(ecex_draw_context_t *ctx, int cell, int alpha) { alpha = tetris_alpha8(alpha); - if (cell == 1) ecex_draw_color_rgba8(ctx, 51, 191, 242, alpha); - else if (cell == 2) ecex_draw_color_rgba8(ctx, 242, 217, 51, alpha); - else if (cell == 3) ecex_draw_color_rgba8(ctx, 179, 89, 242, alpha); - else if (cell == 4) ecex_draw_color_rgba8(ctx, 77, 217, 89, alpha); - else if (cell == 5) ecex_draw_color_rgba8(ctx, 242, 64, 64, alpha); - else if (cell == 6) ecex_draw_color_rgba8(ctx, 64, 102, 242, alpha); - else if (cell == 7) ecex_draw_color_rgba8(ctx, 242, 140, 51, alpha); - else ecex_draw_color_rgba8(ctx, 41, 41, 46, alpha); + if (cell == 1) ecex_draw_color_rgba8_i(ctx, 51, 191, 242, alpha); + else if (cell == 2) ecex_draw_color_rgba8_i(ctx, 242, 217, 51, alpha); + else if (cell == 3) ecex_draw_color_rgba8_i(ctx, 179, 89, 242, alpha); + else if (cell == 4) ecex_draw_color_rgba8_i(ctx, 77, 217, 89, alpha); + else if (cell == 5) ecex_draw_color_rgba8_i(ctx, 242, 64, 64, alpha); + else if (cell == 6) ecex_draw_color_rgba8_i(ctx, 64, 102, 242, alpha); + else if (cell == 7) ecex_draw_color_rgba8_i(ctx, 242, 140, 51, alpha); + else ecex_draw_color_rgba8_i(ctx, 41, 41, 46, alpha); } static void tetris_draw_piece(ecex_draw_context_t *ctx, @@ -386,7 +418,7 @@ static int tetris_tick(ecex_t *ed, buffer_t *buffer, int now_ms, void *userdata) if (!s->game_over && !s->paused && now_ms - s->last_drop_ms >= s->drop_interval_ms) { ecex_log("tetris_tick: dropping piece"); - tetris_soft_drop(ed, s); + tetris_soft_drop(s); s->last_drop_ms = now_ms; return 1; } @@ -413,8 +445,8 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v if (!s) { ecex_log("tetris_draw: null state"); return 0; } if (!ctx) { ecex_log("tetris_draw: null ctx"); return 0; } - board = tetris_board(ed, s); - if (!board) { ecex_log("tetris_draw: no registry board"); return 0; } + board = tetris_board(s); + if (!board) { ecex_log("tetris_draw: no plugin board slot"); return 0; } s->log_draw_count += 1; if (s->log_draw_count <= 8 || (s->log_draw_count % 60) == 0) { ecex_log_int("tetris_draw: count=", s->log_draw_count); @@ -423,7 +455,7 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v } if (s->log_draw_count <= 3) ecex_log("tetris_draw: background"); - ecex_draw_color_rgba8(ctx, 20, 23, 28, 255); + ecex_draw_color_rgba8_i(ctx, 20, 23, 28, 255); ecex_draw_rect_i(ctx, 0, 0, (int)ctx->w, (int)ctx->h); max_board_w = ((int)ctx->content_w * 62) / 100; @@ -438,9 +470,9 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v oy = (int)ctx->content_y + 12; if (s->log_draw_count <= 3) ecex_log("tetris_draw: board frame"); - ecex_draw_color_rgba8(ctx, 8, 9, 12, 255); + ecex_draw_color_rgba8_i(ctx, 8, 9, 12, 255); ecex_draw_rect_i(ctx, ox - 6, oy - 6, board_w + 12, board_h + 12); - ecex_draw_color_rgba8(ctx, 97, 102, 115, 255); + ecex_draw_color_rgba8_i(ctx, 97, 102, 115, 255); ecex_draw_rect_outline_i(ctx, ox - 6, oy - 6, board_w + 12, board_h + 12, 2); if (s->log_draw_count <= 3) ecex_log("tetris_draw: cells"); @@ -459,7 +491,7 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v sy = oy; if (s->log_draw_count <= 3) ecex_log("tetris_draw: sidebar"); - ecex_draw_color_rgba8(ctx, 235, 235, 214, 255); + ecex_draw_color_rgba8_i(ctx, 235, 235, 214, 255); ecex_log("tetris_draw: sidebar title"); ecex_draw_label_i(ctx, sx, sy, 1); sy += ((int)ctx->line_height * 16) / 10; @@ -475,11 +507,11 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v ecex_draw_label_i(ctx, sx, sy, 5); sy += ((int)ctx->line_height * 8) / 10; - ecex_log_int("tetris_draw: preview_piece=", tetris_next_piece(ed, s)); - ecex_draw_tetris_preview_i(ctx, tetris_next_piece(ed, s), sx, sy, (cell * 72) / 100, 255); + ecex_log_int("tetris_draw: preview_piece=", tetris_next_piece(s)); + ecex_draw_tetris_preview_i(ctx, tetris_next_piece(s), sx, sy, (cell * 72) / 100, 255); sy += cell * 4; - ecex_draw_color_rgba8(ctx, 184, 189, 199, 255); + ecex_draw_color_rgba8_i(ctx, 184, 189, 199, 255); ecex_log("tetris_draw: sidebar help"); ecex_draw_label_i(ctx, sx, sy, 6); sy += (int)ctx->line_height; ecex_draw_label_i(ctx, sx, sy, 7); sy += (int)ctx->line_height; @@ -489,9 +521,9 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v ecex_draw_label_i(ctx, sx, sy, 11); if (s->paused || s->game_over) { - ecex_draw_color_rgba8(ctx, 0, 0, 0, 174); + ecex_draw_color_rgba8_i(ctx, 0, 0, 0, 174); ecex_draw_rect_i(ctx, ox, oy + (board_h * 43) / 100, board_w, ((int)ctx->line_height * 22) / 10); - ecex_draw_color_rgba8(ctx, 255, 235, 89, 255); + ecex_draw_color_rgba8_i(ctx, 255, 235, 89, 255); ecex_draw_label_i(ctx, ox + cell, oy + (board_h * 43) / 100 + ((int)ctx->line_height * 55) / 100, s->game_over ? 12 : 13); } @@ -502,8 +534,7 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v static void tetris_free_state(void *userdata) { tetris_state_t *s = (tetris_state_t *)userdata; if (!s) return; - ecex_var_free_owner(s->ed, s); - ecex_config_free(s); + ecex_plugin_object_free(s->plugin, s); } static int cmd_tetris(ecex_t *ed) { @@ -527,25 +558,25 @@ static int cmd_tetris(ecex_t *ed) { ecex_log_ptr("cmd_tetris: existing state=", s); if (!s) { ecex_log_int("cmd_tetris: allocating state bytes=", (int)sizeof(*s)); - s = (tetris_state_t *)ecex_config_calloc(1, sizeof(*s)); + ecex_plugin_t *plugin = ecex_plugin_find(ed, "tetris"); + s = (tetris_state_t *)ecex_plugin_object_calloc(plugin, "state", 1, sizeof(*s)); ecex_log_ptr("cmd_tetris: allocated state=", s); if (!s) { ecex_log("cmd_tetris: allocation failed"); return -1; } - s->ed = ed; - ecex_log_int("cmd_tetris: registry board bytes=", (int)((size_t)TETRIS_CELLS * sizeof(int))); - ecex_log_ptr("cmd_tetris: registry board=", tetris_board(ed, s)); - if (!tetris_board(ed, s)) { - ecex_log("cmd_tetris: registry board allocation failed"); - ecex_var_free_owner(ed, s); - ecex_config_free(s); + s->plugin = plugin; + ecex_log_int("cmd_tetris: board slot bytes=", (int)((size_t)TETRIS_CELLS * sizeof(int))); + ecex_log_ptr("cmd_tetris: board slot=", tetris_board(s)); + if (!tetris_board(s)) { + ecex_log("cmd_tetris: board slot allocation failed"); + ecex_plugin_object_free(plugin, s); return -1; } - tetris_reset(ed, s); + tetris_reset(s); ecex_log("cmd_tetris: setting renderer"); if (ecex_buffer_set_renderer(buffer, tetris_draw, s, tetris_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) { ecex_log("cmd_tetris: set_renderer failed"); - ecex_config_free(s); + ecex_plugin_object_free(plugin, s); return -1; } @@ -577,19 +608,19 @@ static int cmd_tetris_new(ecex_t *ed) { ecex_log("cmd_tetris_new: enter"); tetris_state_t *s = tetris_state_for_ed(ed); if (!s) return cmd_tetris(ed); - tetris_reset(ed, s); + tetris_reset(s); return 0; } static int cmd_tetris_left(ecex_t *ed) { ecex_log("cmd_tetris_left: enter"); - tetris_move_horizontal(ed, tetris_state_for_ed(ed), -1); + tetris_move_horizontal(tetris_state_for_ed(ed), -1); return 0; } static int cmd_tetris_right(ecex_t *ed) { ecex_log("cmd_tetris_right: enter"); - tetris_move_horizontal(ed, tetris_state_for_ed(ed), 1); + tetris_move_horizontal(tetris_state_for_ed(ed), 1); return 0; } @@ -597,7 +628,7 @@ static int cmd_tetris_down(ecex_t *ed) { ecex_log("cmd_tetris_down: enter"); tetris_state_t *s = tetris_state_for_ed(ed); if (s) { - if (tetris_soft_drop(ed, s)) s->score += 1; + if (tetris_soft_drop(s)) s->score += 1; s->last_drop_ms = 0; } return 0; @@ -611,12 +642,12 @@ static int cmd_tetris_rotate(ecex_t *ed) { if (!s || s->game_over || s->paused) return 0; nr = (s->rot + 1) & 3; - if (!tetris_collides(ed, s, s->piece, nr, s->x, s->y)) { + if (!tetris_collides(s, s->piece, nr, s->x, s->y)) { s->rot = nr; - } else if (!tetris_collides(ed, s, s->piece, nr, s->x - 1, s->y)) { + } else if (!tetris_collides(s, s->piece, nr, s->x - 1, s->y)) { --s->x; s->rot = nr; - } else if (!tetris_collides(ed, s, s->piece, nr, s->x + 1, s->y)) { + } else if (!tetris_collides(s, s->piece, nr, s->x + 1, s->y)) { ++s->x; s->rot = nr; } @@ -626,7 +657,7 @@ static int cmd_tetris_rotate(ecex_t *ed) { static int cmd_tetris_drop(ecex_t *ed) { ecex_log("cmd_tetris_drop: enter"); - tetris_hard_drop(ed, tetris_state_for_ed(ed)); + tetris_hard_drop(tetris_state_for_ed(ed)); return 0; } @@ -645,8 +676,9 @@ static int cmd_tetris_quit(ecex_t *ed) { return ecex_execute_command(ed, "quit-window"); } -int ecex_tetris_plugin(ecex_t *ed) { +ECEX_PLUGIN_BEGIN(ecex_tetris_plugin, "tetris") ecex_log("ecex_tetris_plugin: enter"); + (void)plugin; ECEX_CONFIG_MODE(TETRIS_MODE); ECEX_CONFIG_COMMAND("tetris", cmd_tetris); ECEX_CONFIG_COMMAND("tetris-new", cmd_tetris_new); @@ -673,7 +705,7 @@ int ecex_tetris_plugin(ecex_t *ed) { ecex_log("ecex_tetris_plugin: registered all commands and keybinds"); return 0; -} +ECEX_PLUGIN_END #ifndef ECEX_NO_STANDALONE_CONFIG ECEX_CONFIG_BEGIN -- cgit v1.2.3