# `libac`: Acron Client Library A client library written in C, based on [json-c](https://github.com/json-c/json-c) and [wic](https://github.com/cjhdev/wic). ## Building Requirements: * `json-c` installed and using PkgConfig * Git * CMake * A C11 or higher C compiler * Connectivity to this git repository and `github.com` To build on Unix: ```shell git clone https://git.yuuta.moe/Minecraft/acron.git cd acron git submodule update --init cd client/libacron/ mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. make ``` The shared library will be at `libac.so`. The distributable headers are at `client/libacron/include/`. To make debug builds, use the Debug build type. Debug builds will have ASAN enabled. ## Usage All functions begin with `ac_`. Include `libac.h`. ### Thread Safety This library is single-threaded, meaning that all functions should be called on a dedicated thread. However, user may initialize the library on multiple threads and call the functions on different threads without interfering each other. ### Error Handling All error codes are at `common.h`. `AC_E_*` are library errors (generated by library routines); `AC_ER_*` are remote errors (generated by the server), and you should match them with `ac_response_error.code`. All libac functions return an integer value of the error code. `AC_E_OK(0)` indicates successful operation. Any library function calls not following the document will return `AC_E_INVALID_REQUEST`. ### Struct Inheritance All libac exported structs have a `uint8_t type` as its first member. It indicates the type of the object. The type ID is globally unique and stable across upgrades. Internally, the type is an unsigned 8-bit integer, with the first two bits indicating whether it is an event, a response, or a request. The remaining 6 bits differentiates the type with others. Library users usually do not need to care about the internal assignment of type IDs. Every exported struct have its ID defined in the corresponding headers (e.g. `AC_RESPONSE_OK`). Macros like `AC_IS_EVENT` is also available (from `ids.h`). Thus, the base definition of all libac structs is: ```c /* ids.h */ typedef struct ac_obj { uint8_t type; } ac_obj_t; ``` are classified in three types: * Event: Server-initiated events. Do not have IDs. Base definition: `ac_event_t`. ```c /* events.h */ typedef struct ac_event { uint8_t type; } ac_event_t; ``` * Request: Program-allocated requests. Have a program-assigned ID. Base definition: `ac_request_t`. ```c /* requests.h */ typedef struct ac_request { uint8_t type; int id; } ac_request_t; ``` * Response: Responses to requests. Have the same ID as the request. Base definition: `ac_response_t`. ```c /* requests.h */ typedef struct ac_response { uint8_t type; int id; } ac_response_t; ``` ### Struct Memory Management For requests only, it is the program's responsibility to allocate them (on whether stack or heap) and assign the correct type, so libac can recognize them. The program should also free the request after making `ac_request()` calls, as libac will not modify the request object: ```c int list(void *connection) { /* Allocated on the stack: Freed upon return, not by libac. */ ac_request_cmd cmd = { .type = AC_REQUEST_CMD, .id = 114514, .config = NULL, .cmd = "list" }; int r = ac_request(connection, (ac_request_t *) &cmd); /* cmd is freed. */ return r; } ``` For events and responses, libac will allocate them internally, and the program should free them using `ac_obj_free(ac_obj_t)` function. ### Initialization To initialize the library on the current thread, call: `ac_init()`: ```c int main(void) { libac_config_t config = { .out = NULL, /* Error output stream. NULL to disable any error logging. */ .tok = NULL } int r = ac_init(&config); if (r) return r; } ``` When finished using libac on the thread, call `ac_free()`. Any function call without `ac_init()` first will return `AC_E_NOT_INITIALIZED`. ### Making Connections To connect to a server on the current thread, use `ac_connect()`. It will output a `void *` opaque pointer referencing to the connection. Programs should pass this pointer as-is when making connections or disconnecting. Every thread can have multiple connections simultaneously. ```c void *connection = NULL; int r; ac_connection_parameters_t args = { .url = "ws://localhost:25575/ws?id=1&token=123" }; if ((r = ac_connect(args, &connection))) { return r; } /* Listen or make requests. */ ``` Then, the program can listen for responses or events: ```c ac_obj_t *obj = NULL; while (!interrupted || !(r = ac_receive(connection, &obj))) { /* The obj is now referencing to a library allocated event or response. */ /* Do things with the event or response. */ ac_obj_free(obj); } ``` The program can make requests using `ac_request()`. ~~This function can be called from any thread (not specific to the listening one), but any thread calling this function must also call `ac_init()` first.~~ **On the roadmap.** Finally, when done, call `ac_disconnect()`. It will close the socket and free the `connection` parameter. The `connection` pointer after the method call is undefined. > **Notes**: > > Only `ws://` is supported at this time. `wss://` support is > on the roadmap. Read more: `acronc/main.c` example client. ## Roadmap * Make unit tests * SSL support * Calling `ac_request()` from any thread ## License The libac library is licensed under LGPL-2.1-only. Other parts of Acron is still licensed under GPL-2.0-only.