385 lines
19 KiB
Markdown
385 lines
19 KiB
Markdown
|
|
# 21. Appendix: limits, statuses and properties
|
|||
|
|
|
|||
|
|
Three reference tables that no single chapter owns: which function raises which status,
|
|||
|
|
every compile-time bound, and every configuration property the library reads.
|
|||
|
|
|
|||
|
|
The constant tables come in as `excerpt=` blocks against their headers, so they cannot
|
|||
|
|
drift from the values the library was built with — and so the hand-aligned columns those
|
|||
|
|
headers maintain survive.
|
|||
|
|
|
|||
|
|
## A. Status cross-reference
|
|||
|
|
|
|||
|
|
**What this table is.** The statuses a caller can expect to see out of each function. It is
|
|||
|
|
built from the `FAIL_*` and `HANDLE*` sites in `src/` — the function's own body and the
|
|||
|
|
`static` helpers only it calls — plus, where a nested libakgl call is part of the documented
|
|||
|
|
contract, that status too.
|
|||
|
|
|
|||
|
|
**It is not exhaustive, and cannot be.** Every libakgl function a given one calls can
|
|||
|
|
propagate anything from its own row, and so can libakstdlib: nearly everything that touches
|
|||
|
|
a file also carries whatever `aksl_fopen`, `aksl_fread`, `aksl_fwrite` and `aksl_fclose`
|
|||
|
|
report, including bare `errno` values such as `ENOENT`, `EACCES` and `ENOSPC`. Use the table
|
|||
|
|
to write the `HANDLE` arms you care about, not to prove a status cannot arrive.
|
|||
|
|
|
|||
|
|
`(H)` marks a status the function **handles** rather than raises.
|
|||
|
|
|
|||
|
|
What the statuses mean is [Chapter 4](04-errors.md).
|
|||
|
|
|
|||
|
|
### `error.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_error_init` | `AKERR_STATUS_RANGE_OVERLAP`, `AKERR_STATUS_RANGE_FULL`, `AKERR_STATUS_NAME_FULL` (all libakerror's) |
|
|||
|
|
|
|||
|
|
### `heap.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_heap_init`, `akgl_heap_init_actor` | *nothing — no failure path today* |
|
|||
|
|
| `akgl_heap_next_actor`, `_sprite`, `_spritesheet`, `_character`, `_string` | `AKGL_ERR_HEAP` |
|
|||
|
|
| `akgl_heap_release_actor`, `_sprite`, `_spritesheet`, `_character`, `_string` | `AKERR_NULLPOINTER` |
|
|||
|
|
|
|||
|
|
### `registry.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_registry_init` and the eight `akgl_registry_init_*` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_registry_load_properties` | `AKERR_NULLPOINTER`, `AKERR_KEY`, `AKERR_TYPE`, `AKGL_ERR_HEAP` |
|
|||
|
|
| `akgl_set_property` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_get_property` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS`, `AKERR_VALUE`, `AKGL_ERR_HEAP` |
|
|||
|
|
|
|||
|
|
### `game.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_game_init` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_game_update_fps`, `akgl_game_lowfps` | *`void` — cannot report* |
|
|||
|
|
| `akgl_game_state_lock` | `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_game_state_unlock` | *nothing* |
|
|||
|
|
| `akgl_game_update` | `AKGL_ERR_SDL`, plus whatever `updatefunc`, `akgl_tilemap_scale_actor`, the physics backend or the renderer raises |
|
|||
|
|
| `akgl_game_save` | `AKERR_NULLPOINTER`, `AKERR_IO`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_game_save_actors` | `AKERR_NULLPOINTER`, `AKERR_IO` |
|
|||
|
|
| `akgl_game_load` | `AKERR_NULLPOINTER`, `AKERR_API`, `AKERR_EOF`, `AKERR_IO`, `AKERR_VALUE` |
|
|||
|
|
| `akgl_game_load_versioncmp` | `AKERR_NULLPOINTER`, `AKERR_VALUE`, `AKERR_API` |
|
|||
|
|
|
|||
|
|
### `actor.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_actor_initialize` | `AKERR_NULLPOINTER`, `AKERR_KEY` |
|
|||
|
|
| `akgl_actor_set_character` | `AKERR_NULLPOINTER` — **including "no such character"** |
|
|||
|
|
| `akgl_actor_add_child` | `AKERR_NULLPOINTER`, `AKERR_RELATIONSHIP`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_actor_update` | `AKERR_NULLPOINTER`, `AKERR_KEY` (H) |
|
|||
|
|
| `akgl_actor_render` | `AKERR_NULLPOINTER`, `AKERR_KEY` (H), `AKERR_OUTOFBOUNDS` (H) |
|
|||
|
|
| `akgl_actor_automatic_face`, `_logic_movement`, `_logic_changeframe` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_actor_cmhf_*_on`, `akgl_actor_cmhf_*_off` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_registry_iterate_actor` | `AKERR_NULLPOINTER`, `AKERR_KEY` — **`void`; unhandled means the process exits** |
|
|||
|
|
|
|||
|
|
### `character.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_character_initialize` | `AKERR_NULLPOINTER`, `AKERR_KEY` |
|
|||
|
|
| `akgl_character_sprite_add` | `AKERR_NULLPOINTER`, `AKERR_KEY` |
|
|||
|
|
| `akgl_character_sprite_get` | `AKERR_NULLPOINTER`, `AKERR_KEY` |
|
|||
|
|
| `akgl_character_load_json` | `AKERR_NULLPOINTER`, `AKERR_KEY` for a state name that is not in `AKGL_REGISTRY_ACTOR_STATE_STRINGS`, and `AKERR_TYPE` from the JSON accessors |
|
|||
|
|
| `akgl_character_state_sprites_iterate` | `AKERR_NULLPOINTER` — **`void`; unhandled means the process exits** |
|
|||
|
|
|
|||
|
|
### `sprite.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_sprite_initialize` | `AKERR_NULLPOINTER`, `AKERR_KEY` |
|
|||
|
|
| `akgl_sprite_load_json` | `AKERR_NULLPOINTER`, `AKERR_VALUE`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_spritesheet_initialize` | `AKERR_NULLPOINTER`, `AKERR_KEY`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_spritesheet_coords_for_frame` | `AKERR_NULLPOINTER` |
|
|||
|
|
|
|||
|
|
### `json_helpers.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_get_json_object_value`, `_boolean_`, `_integer_`, `_number_`, `_double_`, `_string_`, `_array_value` | `AKERR_NULLPOINTER`, **`AKERR_KEY` for absent**, **`AKERR_TYPE` for wrong type** |
|
|||
|
|
| `akgl_get_json_array_index_object`, `_integer`, `_string` | `AKERR_NULLPOINTER`, **`AKERR_OUTOFBOUNDS` for a short array**, `AKERR_TYPE` |
|
|||
|
|
| `akgl_get_json_with_default` | `AKERR_NULLPOINTER`; handles `AKERR_KEY` (H), `AKERR_OUTOFBOUNDS` (H), `AKERR_INDEX` (H) |
|
|||
|
|
|
|||
|
|
`akgl_get_json_string_value` and `akgl_get_json_array_index_string` also raise
|
|||
|
|
`AKGL_ERR_HEAP` when they have to claim a pooled string and the pool is empty.
|
|||
|
|
|
|||
|
|
### `physics.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_physics_init_null`, `akgl_physics_init_arcade` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_physics_factory` | `AKERR_NULLPOINTER`, **`AKERR_KEY` for an unknown backend name** |
|
|||
|
|
| `akgl_physics_simulate` | `AKERR_NULLPOINTER`; handles `AKGL_ERR_LOGICINTERRUPT` (H) |
|
|||
|
|
| `akgl_physics_null_gravity`, `_collide`, `_move` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_physics_arcade_gravity`, `_move` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_physics_arcade_collide` | `AKERR_NULLPOINTER`, **`AKERR_API` — not implemented** |
|
|||
|
|
|
|||
|
|
### `renderer.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_render_2d_init` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL`, plus `akgl_get_property`'s |
|
|||
|
|
| `akgl_render_2d_bind`, `_shutdown`, `_frame_start`, `_frame_end`, `_draw_texture`, `_draw_world` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_render_2d_draw_mesh` | **`AKERR_API` — not implemented** |
|
|||
|
|
|
|||
|
|
### `draw.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_draw_point`, `_line`, `_rect`, `_filled_rect` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_draw_background` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_draw_circle` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_draw_flood_fill` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL`, `AKERR_OUTOFBOUNDS` past `AKGL_DRAW_MAX_FLOOD_SPANS` |
|
|||
|
|
| `akgl_draw_copy_region`, `_paste_region` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
|
|||
|
|
|
|||
|
|
### `tilemap.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_tilemap_load` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_tilemap_load_layers` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_tilemap_load_layer_tile` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_tilemap_load_layer_image` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_tilemap_load_layer_objects` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_tilemap_load_layer_object_actor` | `AKERR_KEY` |
|
|||
|
|
| `akgl_tilemap_load_tilesets` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` past `AKGL_TILEMAP_MAX_TILESETS` |
|
|||
|
|
| `akgl_tilemap_load_tilesets_each` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_tilemap_load_physics` | handles `AKERR_KEY` (H) — every physics property is optional |
|
|||
|
|
| `akgl_get_json_tilemap_property` | `AKERR_NULLPOINTER`, `AKERR_KEY`, `AKERR_TYPE` |
|
|||
|
|
| `akgl_tilemap_draw`, `_draw_tileset` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_tilemap_scale_actor`, `akgl_tilemap_release` | `AKERR_NULLPOINTER` |
|
|||
|
|
|
|||
|
|
### `controller.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_controller_open_gamepads`, `_list_keyboards` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_controller_handle_event`, `_button_down`, `_button_up`, `_added`, `_removed` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_controller_pushmap` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_controller_default` | `AKERR_OUTOFBOUNDS`, **`AKGL_ERR_REGISTRY`** — the library's only site |
|
|||
|
|
| `akgl_controller_poll_key`, `_poll_keystroke` | `AKERR_NULLPOINTER` |
|
|||
|
|
|
|||
|
|
### `text.h`, `audio.h`, `assets.h`, `util.h`, `staticstring.h`
|
|||
|
|
|
|||
|
|
| Function | Raises |
|
|||
|
|
|---|---|
|
|||
|
|
| `akgl_text_loadfont` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_text_unloadfont` | `AKERR_NULLPOINTER`, `AKERR_KEY` |
|
|||
|
|
| `akgl_text_rendertextat` | `AKERR_NULLPOINTER` — including a `NULL` `akgl_renderer`, a `NULL` `sdl_renderer`, and a backend never run through `akgl_render_2d_bind` and so missing `draw_texture`. An **empty** string is not an error: it returns success having drawn nothing |
|
|||
|
|
| `akgl_text_measure`, `_measure_wrapped` | `AKERR_NULLPOINTER`. An empty string is legal and measures 0 wide by one line high |
|
|||
|
|
| `akgl_audio_tone`, `akgl_audio_sweep` | `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_audio_voice_active`, `akgl_audio_mix` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_load_start_bgm` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_path_relative` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
|
|||
|
|
| `akgl_rectangle_points`, `akgl_collide_point_rectangle`, `akgl_collide_rectangles` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_compare_sdl_surfaces` | `AKERR_NULLPOINTER`, `AKERR_VALUE` |
|
|||
|
|
| `akgl_render_and_compare` | `AKERR_NULLPOINTER`, `AKERR_IO`, `AKGL_ERR_SDL` |
|
|||
|
|
| `akgl_string_initialize` | `AKERR_NULLPOINTER` |
|
|||
|
|
| `akgl_string_copy` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS`, `AKERR_VALUE` |
|
|||
|
|
|
|||
|
|
## B. Compile-time limits
|
|||
|
|
|
|||
|
|
Everything below is fixed when the library is compiled. **Only the `AKGL_MAX_HEAP_*` five
|
|||
|
|
are overridable**, and even those have to be overridden for the whole build — see
|
|||
|
|
[Chapter 5](05-the-heap.md), "The ceilings are a compile-time ABI constraint". The rest are
|
|||
|
|
plain `#define`s with no `#ifndef` guard: changing one means editing the header and
|
|||
|
|
rebuilding libakgl and everything linking it.
|
|||
|
|
|
|||
|
|
### The object pools
|
|||
|
|
|
|||
|
|
```c excerpt=include/akgl/heap.h
|
|||
|
|
#ifndef AKGL_MAX_HEAP_ACTOR
|
|||
|
|
#define AKGL_MAX_HEAP_ACTOR 64
|
|||
|
|
#endif
|
|||
|
|
#ifndef AKGL_MAX_HEAP_SPRITE
|
|||
|
|
#define AKGL_MAX_HEAP_SPRITE (AKGL_MAX_HEAP_ACTOR * 16)
|
|||
|
|
#endif
|
|||
|
|
#ifndef AKGL_MAX_HEAP_SPRITESHEET
|
|||
|
|
#define AKGL_MAX_HEAP_SPRITESHEET AKGL_MAX_HEAP_SPRITE
|
|||
|
|
#endif
|
|||
|
|
#ifndef AKGL_MAX_HEAP_CHARACTER
|
|||
|
|
#define AKGL_MAX_HEAP_CHARACTER 256
|
|||
|
|
#endif
|
|||
|
|
#ifndef AKGL_MAX_HEAP_STRING
|
|||
|
|
#define AKGL_MAX_HEAP_STRING 256
|
|||
|
|
#endif
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Exceeding any of them is `AKGL_ERR_HEAP`.
|
|||
|
|
|
|||
|
|
### Strings
|
|||
|
|
|
|||
|
|
`AKGL_MAX_STRING_LENGTH` is `PATH_MAX`, which is 4096 on Linux. That is the capacity of
|
|||
|
|
every `akgl_String`, and it is why the string pool is over a megabyte on its own.
|
|||
|
|
|
|||
|
|
### Actors
|
|||
|
|
|
|||
|
|
```c excerpt=include/akgl/actor.h
|
|||
|
|
/** @brief Longest actor name, including the terminator. Names are truncated, not rejected. */
|
|||
|
|
#define AKGL_ACTOR_MAX_NAME_LENGTH 128
|
|||
|
|
/** @brief Children one actor can carry. A child moves with its parent rather than simulating. */
|
|||
|
|
#define AKGL_ACTOR_MAX_CHILDREN 8
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`AKGL_ACTOR_MAX_STATES` is 32, fixed by the width of `int32_t state` rather than chosen.
|
|||
|
|
A ninth child is `AKERR_OUTOFBOUNDS`; an over-long name is silently truncated.
|
|||
|
|
|
|||
|
|
### Sprites and spritesheets
|
|||
|
|
|
|||
|
|
```c excerpt=include/akgl/sprite.h
|
|||
|
|
#define AKGL_SPRITE_MAX_FRAMES 16
|
|||
|
|
#define AKGL_SPRITE_MAX_NAME_LENGTH 128
|
|||
|
|
#define AKGL_SPRITE_MAX_REGISTRY_SIZE 1024
|
|||
|
|
#define AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH 512
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`akgl_sprite_load_json` bounds the `frames` array against `AKGL_SPRITE_MAX_FRAMES` before
|
|||
|
|
writing anything, and refuses a frame number that will not fit a `uint8_t` rather than
|
|||
|
|
truncating it into an index naming a different tile (`TODO.md` item 16).
|
|||
|
|
|
|||
|
|
**`AKGL_SPRITE_MAX_REGISTRY_SIZE` is dead.** It is defined in `sprite.h` and referenced
|
|||
|
|
nowhere in `src/`, `include/`, `tests/` or `util/`. It bounds nothing. Do not size anything
|
|||
|
|
against it.
|
|||
|
|
|
|||
|
|
### Tilemaps
|
|||
|
|
|
|||
|
|
```c excerpt=include/akgl/tilemap.h
|
|||
|
|
/** @brief Widest map, in tiles. Width times height is what is actually bounded. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_WIDTH 512
|
|||
|
|
/** @brief Tallest map, in tiles. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_HEIGHT 512
|
|||
|
|
/** @brief Layers per map. Also the number of draw passes akgl_render_2d_draw_world makes. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_LAYERS 16
|
|||
|
|
/** @brief Tilesets per map. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_TILESETS 16
|
|||
|
|
/** @brief Entries in a tileset's offset table. Indexed by *local* tile id, so a tileset with a high `firstgid` still starts at 0. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_TILES_PER_IMAGE 65536
|
|||
|
|
/** @brief Longest tileset name. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_TILESET_NAME_SIZE 512
|
|||
|
|
/** @brief Longest resolved tileset image path. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE PATH_MAX
|
|||
|
|
/** @brief Longest object name. Note that an object naming an actor is truncated at #AKGL_ACTOR_MAX_NAME_LENGTH (128) instead. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_OBJECT_NAME_SIZE 512
|
|||
|
|
/** @brief Objects in one object layer. Not enforced by the loader -- a longer group writes past the array. */
|
|||
|
|
#define AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER 128
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Three corrections to what those comments say, all verified against `src/tilemap.c`:
|
|||
|
|
|
|||
|
|
- **`AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER` *is* enforced.** The comment above says it is not.
|
|||
|
|
`akgl_tilemap_load_layer_objects` bounds `j` at the top of the loop body and raises
|
|||
|
|
`AKERR_OUTOFBOUNDS`; `akgl_tilemap_load_tilesets` does the same for
|
|||
|
|
`AKGL_TILEMAP_MAX_TILESETS`. Both landed in 0.5.0 (`TODO.md`, "Known and still open"
|
|||
|
|
item 17), and the header comment was not updated with them.
|
|||
|
|
- **Width and height are not bounded individually.** `akgl_tilemap_load` checks
|
|||
|
|
`width * height >= AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT`, and the comparison
|
|||
|
|
is `>=`, so the true ceiling is **262143 tiles** in any shape. A 1024×256 map loads; a
|
|||
|
|
512×512 one does not.
|
|||
|
|
- **`AKGL_TILEMAP_MAX_TILES_PER_IMAGE` is not checked anywhere.** It sizes
|
|||
|
|
`tile_offsets[65536][2]` in the struct and nothing validates a tileset against it. It is
|
|||
|
|
also why `akgl_Tileset` is large: 512 KiB of offset table per tileset, whatever the image
|
|||
|
|
actually holds.
|
|||
|
|
|
|||
|
|
### Controllers
|
|||
|
|
|
|||
|
|
```c excerpt=include/akgl/controller.h
|
|||
|
|
/** @brief How many control maps exist -- effectively the local player limit. */
|
|||
|
|
#define AKGL_MAX_CONTROL_MAPS 8
|
|||
|
|
/** @brief Bindings per control map. The default map installed by akgl_controller_default uses 8 of them. */
|
|||
|
|
#define AKGL_MAX_CONTROLS 32
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`AKGL_CONTROLLER_KEY_BUFFER` is 32 — the keystroke ring, sized so a program polling once a
|
|||
|
|
frame never loses a key to a fast typist. `AKGL_CONTROLLER_KEYSTROKE_TEXT` is 8, enough for
|
|||
|
|
one UTF-8 code point and its terminator; a longer commit is truncated **on a code point
|
|||
|
|
boundary** rather than through the middle of one.
|
|||
|
|
|
|||
|
|
### Drawing
|
|||
|
|
|
|||
|
|
`AKGL_DRAW_MAX_FLOOD_SPANS` is 4096 — the fixed stack of horizontal runs `akgl_draw_flood_fill`
|
|||
|
|
keeps instead of recursing per pixel. A region needing more pending runs at once reports
|
|||
|
|
`AKERR_OUTOFBOUNDS` rather than overflowing; an ordinary convex or moderately concave shape
|
|||
|
|
needs a few dozen.
|
|||
|
|
|
|||
|
|
### Audio
|
|||
|
|
|
|||
|
|
```c excerpt=include/akgl/audio.h
|
|||
|
|
/** @brief Sample rate of the generated stream, in frames per second. */
|
|||
|
|
#define AKGL_AUDIO_SAMPLE_RATE 44100
|
|||
|
|
|
|||
|
|
/** @brief Frames the device callback generates per pass through the mixer. */
|
|||
|
|
#define AKGL_AUDIO_MIX_FRAMES 512
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`AKGL_AUDIO_MAX_VOICES` is 3. `AKGL_AUDIO_SWEEP_TICK_HZ` is 60, and it divides the sample
|
|||
|
|
rate exactly, so a sweep step boundary always lands on a whole frame —
|
|||
|
|
`AKGL_AUDIO_SWEEP_TICK_FRAMES` is 735.
|
|||
|
|
|
|||
|
|
The SDL_mixer side is separate: `AKGL_GAME_AUDIO_MAX_TRACKS` is 64 playback tracks, and
|
|||
|
|
**slot 0 is unused** — `AKGL_GAME_AUDIO_TRACK_BGM` is slot 1, and the rest are yours.
|
|||
|
|
|
|||
|
|
### Time and the frame
|
|||
|
|
|
|||
|
|
| Constant | Value | Meaning |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `AKGL_TIME_ONESEC_NS` | 1000000000 | Nanoseconds in a second — the unit `SDL_GetTicksNS` reports in |
|
|||
|
|
| `AKGL_TIME_ONEMS_NS` | 1000000 | Nanoseconds in a millisecond — the scale factor from JSON frame durations to internal ones |
|
|||
|
|
| `AKGL_GAME_STATE_LOCK_BUDGET_MS` | 1000 | How long `akgl_game_state_lock` keeps trying before raising |
|
|||
|
|
| `AKGL_GAME_STATE_LOCK_RETRY_MS` | 100 | How long it sleeps between attempts |
|
|||
|
|
| `AKGL_PHYSICS_DEFAULT_MAX_TIMESTEP` | 0.05 | Default `physics.max_timestep`, in seconds — three frames at 60 Hz |
|
|||
|
|
|
|||
|
|
`AKGL_TIME_ONEMS_NS` was called `AKGL_TIME_ONESEC_MS` until 0.5.0 and held 1000000 — a name
|
|||
|
|
and a value describing two different quantities. The state lock read it as a one-second
|
|||
|
|
budget and was wrong by a factor of a thousand, blocking for roughly sixteen minutes.
|
|||
|
|
|
|||
|
|
### The status band
|
|||
|
|
|
|||
|
|
| Constant | Value |
|
|||
|
|
|---|---|
|
|||
|
|
| `AKGL_ERR_BASE` | 256 (`AKERR_FIRST_CONSUMER_STATUS`) |
|
|||
|
|
| `AKGL_ERR_COUNT` | 5 |
|
|||
|
|
| `AKGL_ERR_LIMIT` | 261 — **the one-past-the-end sentinel, not a status** |
|
|||
|
|
|
|||
|
|
## C. Configuration properties
|
|||
|
|
|
|||
|
|
Everything the library reads out of `AKGL_REGISTRY_PROPERTIES`. **Every value is a string**,
|
|||
|
|
including the numbers, and each is parsed by whoever reads it.
|
|||
|
|
|
|||
|
|
| Property | Default | Read by | Read when | Meaning |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| `game.screenwidth` | `"0"` | `akgl_render_2d_init` | once, at init | Window and camera width in pixels. Passed straight to `SDL_CreateWindowAndRenderer` |
|
|||
|
|
| `game.screenheight` | `"0"` | `akgl_render_2d_init` | once, at init | Window and camera height in pixels |
|
|||
|
|
| `physics.gravity.x` | `"0.0"` | `akgl_physics_init_arcade` | once, at init | Constant acceleration on x, px/s² |
|
|||
|
|
| `physics.gravity.y` | `"0.0"` | `akgl_physics_init_arcade` | once, at init | Constant acceleration on y, px/s². Positive is down |
|
|||
|
|
| `physics.gravity.z` | `"0.0"` | `akgl_physics_init_arcade` | once, at init | Constant acceleration on z, px/s² |
|
|||
|
|
| `physics.drag.x` | `"0.0"` | `akgl_physics_init_arcade` | once, at init | Fraction of environmental velocity shed per second on x |
|
|||
|
|
| `physics.drag.y` | `"0.0"` | `akgl_physics_init_arcade` | once, at init | Same for y. **The only brake on falling** — there is no terminal velocity, and `ey` approaches `gravity_y / drag_y` |
|
|||
|
|
| `physics.drag.z` | `"0.0"` | `akgl_physics_init_arcade` | once, at init | Same for z |
|
|||
|
|
| `physics.max_timestep` | `"0.05"` | `akgl_physics_init_arcade` | once, at init | Ceiling on a single simulation `dt`, in seconds |
|
|||
|
|
|
|||
|
|
That is the complete list — nine properties, read by two functions, each exactly once.
|
|||
|
|
|
|||
|
|
**`physics.engine` is not read by anything.** `physics.h` says in two places that
|
|||
|
|
`akgl_game_init` passes it to `akgl_physics_factory`; `akgl_game_init` never calls the
|
|||
|
|
factory, and the string does not appear in `src/`. Pass `"null"` or `"arcade"` to
|
|||
|
|
`akgl_physics_factory` yourself. Correcting the header is a source change and is deliberately
|
|||
|
|
out of scope for this manual.
|
|||
|
|
|
|||
|
|
**Everything is read at init.** Setting any of these after `akgl_render_2d_init` or
|
|||
|
|
`akgl_physics_init_arcade` has no effect at all, and reports success. See
|
|||
|
|
[Chapter 6](06-the-registry.md), and the startup order in
|
|||
|
|
[Chapter 7](07-the-game-and-the-frame.md).
|
|||
|
|
|
|||
|
|
**A property set before `AKGL_REGISTRY_PROPERTIES` exists is silently discarded.**
|
|||
|
|
`akgl_set_property` does not check SDL's return value. `akgl_game_init` and
|
|||
|
|
`akgl_registry_init` both create the registry; a hand-rolled startup that creates neither
|
|||
|
|
gets no configuration and no error.
|
|||
|
|
|
|||
|
|
## Where to go next
|
|||
|
|
|
|||
|
|
- [Chapter 4](04-errors.md) — what each status means.
|
|||
|
|
- [Chapter 5](05-the-heap.md) — the pools, and the ABI constraint on their ceilings.
|
|||
|
|
- [Chapter 6](06-the-registry.md) — the property store these live in.
|
|||
|
|
- The generated Doxygen reference — every function's own `@throws` list and full signature.
|