From 7099a86ca74fa637f26af38674f80fb8efd5f6fa Mon Sep 17 00:00:00 2001 From: Trumeet Date: Tue, 26 Jul 2022 17:36:20 -0700 Subject: refactor(libacron/acronc/helloworld): move to separate directories The corresponding CMakeLists.txt files are still rough. --- client/helloworld/main.c | 370 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 client/helloworld/main.c (limited to 'client/helloworld/main.c') diff --git a/client/helloworld/main.c b/client/helloworld/main.c new file mode 100644 index 0000000..c112fcb --- /dev/null +++ b/client/helloworld/main.c @@ -0,0 +1,370 @@ +/* + * Created by yuuta on 7/20/22. + */ + +#include "libac.h" +#include "net.h" + +#include + +#ifndef WIN32 + +#include +#include +#include + +#endif + +#ifdef __STDC_NO_THREADS__ +# ifdef WIN32 +#define THREAD_WIN32 +#include +static HANDLE mtx_conn; + +int lock(void) { \ +DWORD _dwlck = WaitForSingleObject(mtx_conn, 0); \ +if (_dwlck == WAIT_FAILED) { _dwlck = GetLastError(); } \ +if (_dwlck) { fprintf(stderr, "Cannot lock the mutex: %d.\n", _dwlck); return _dwlck; } \ +return 0; \ +} + +int unlock(void) { \ +if (!ReleaseMutex(mtx_conn)) { \ +int r = GetLastError(); \ +fprintf(stderr, "Cannot release the mutex: %d.\n", r); \ +return r; \ +} \ +return 0; \ +} + +# else +#error "Either C11 threading or Win32 API is required for concurrency." +# endif +#else + +#define THREAD_C11 + +#include + +static mtx_t mtx_conn; + +int lock(void) { + \ +if (mtx_lock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot lock the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +int unlock(void) { + \ +if (mtx_unlock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot release the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +#endif + +static const char *world_name(const enum ac_world world) { + switch (world) { + case overworld: + return "overworld"; + case nether: + return "nether"; + case end: + return "end"; + default: + return "unknown world"; + } +} + +static void handle_event(const ac_event_t *event) { + switch (event->type) { + case AC_EVENT_LAGGING: { + ac_event_lagging_t *o = (ac_event_lagging_t *) event; + printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", + o->ms, + o->ticks); + break; + } + case AC_EVENT_ENTITY_DEATH: { + ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; + printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", + o->entity.name, + world_name(o->entity.world), + o->entity.pos.x, + o->entity.pos.y, + o->entity.pos.z, + o->message); + break; + } + case AC_EVENT_PLAYER_MESSAGE: { + ac_event_player_message_t *o = (ac_event_player_message_t *) event; + printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->text); + break; + } + case AC_EVENT_PLAYER_DISCONNECT: { + ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; + printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->reason); + break; + } + case AC_EVENT_PLAYER_JOIN: { + ac_event_player_join_t *o = (ac_event_player_join_t *) event; + printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z); + break; + } + default: { + printf("Received an unrecognized event of type '%u'.\n", + event->type); + } + } +} + +static void handle_response(const ac_response_t *response) { + switch (response->type) { + case AC_RESPONSE_OK: { + ac_response_ok_t *o = (ac_response_ok_t *) response; + printf("Request %d OK.\n", + o->id); + break; + } + case AC_RESPONSE_ERROR: { + ac_response_error_t *o = (ac_response_error_t *) response; + printf("Request %d failed: %s (%d).\n", + o->id, + o->message, + o->code); + break; + } + case AC_RESPONSE_CMD_OUT: { + ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; + printf("Request %d output by %s: %s.\n", + o->id, + o->sender, + o->out); + break; + } + case AC_RESPONSE_CMD_RESULT: { + ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; + printf("Request %d is done: %s (%d).\n", + o->id, + o->success ? "Success" : "Failed", + o->result); + break; + } + default: { + printf("Received an unrecognized response of type '%u'.\n", + response->type); + } + } +} + +#ifndef WIN32 + +static void intr(int signum) {} + +#endif + +static int on_send(const void *sock, + const void *buffer, + const size_t len) { + int r; + printf("Writing %lu bytes.\n", len); + if ((r = net_send(sock, buffer, len))) { + fprintf(stderr, "Cannot write %lu bytes to server: %s (%d).\n", + len, + net_strerror(r), + r); + } + return r; +} + +static int say(void *connection) { + int r; + ac_config_t conf = { + .name = "helloworld" + }; + ac_request_cmd_t req = { + .type = AC_REQUEST_CMD, + .id = 100, + .config = &conf, + .cmd = "say Hi" + }; + if ((r = ac_request(connection, (ac_request_t *) &req))) { + return r; + } + return 0; +} + +int main(int argc, char **argv) { + int r; + if ((r = net_init())) { + return r; + } +#if defined(THREAD_WIN32) + if (!(mtx_conn = CreateMutex(NULL, TRUE, NULL))) { + r = GetLastError(); + fprintf(stderr, "Cannot create mutex: %d.\n", r); + return r; + } +#elif defined(THREAD_C11) + if (mtx_init(&mtx_conn, mtx_plain) != thrd_success) { + fprintf(stderr, "Cannot create mutex.\n"); + return 1; + } +#endif + +#ifdef WIN32 + fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); +#else + struct sigaction sa; + sa.sa_handler = intr; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGINT, &sa, NULL) || + sigaction(SIGTERM, &sa, NULL)) { + const int e = errno; + fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", + strerror(e), + e); + return e; + } +#endif + libac_config_t config = { +#ifdef DEBUG + .out = stderr, +#else + .out = NULL, +#endif + .tok = NULL + }; + if ((r = ac_init(&config))) return r; + void *connection; + SOCKET sock; + ac_connection_parameters_t parameters = { + .host = "localhost", + .port = 25575, + .id = "1", + .token = "123", + .version = 0, + .sock = NULL, + .on_send = on_send + }; + if ((r = net_connect(parameters.host, parameters.port, &sock))) { + fprintf(stderr, "Cannot connect to server: %s (%d).\n", + net_strerror(r), + r); + ac_free(); + net_free(); + return r; + } + parameters.sock = &sock; + if ((r = ac_connect(parameters, &connection))) { + net_close(&sock); + ac_free(); + net_free(); + return r; + } + ac_obj_t *obj; + int nr; + uint8_t buffer[1000U]; + size_t bytes; + enum ac_connection_state state; + printf("Waiting until the connection is established.\n"); + bool ready = false; + while (1) { + if ((nr = net_read(&sock, buffer, sizeof(buffer), &bytes, 10))) { + if (nr == NET_TIMEOUT) { + printf("Receive timeout.\n"); + continue; + } else { + goto end; + } + } + if ((r = lock())) { goto end; } + if ((r = ac_receive(connection, buffer, bytes, &obj))) { + if ((r = unlock())) { goto end; } + goto end; + } + if (!ready) { + /* Wait until ready. */ + if ((r = ac_get_state(connection, &state))) { + if ((r = unlock())) { goto end; } + goto end; + } + switch (state) { + case AC_STATE_INIT: + continue; + case AC_STATE_READY: + printf("Connection is established.\n"); + ready = true; + say(connection); + if ((r = unlock())) { goto end; } + continue; + default: + fprintf(stderr, "Unexpected state.\n"); + if ((r = unlock())) { goto end; } + goto end; + } + } + if ((r = unlock())) { goto end; } + + if (!obj) { + continue; + } + if (AC_IS_EVENT(obj->type)) { + handle_event((ac_event_t *) obj); + } else if (AC_IS_RESPONSE(obj->type)) { + handle_response((ac_response_t *) obj); + } + ac_object_free(obj); + } + end: + if (nr) { + if (nr == NET_CLOSED) { + fprintf(stderr, "Connection is closed while reading from the server.\n"); + } else { + fprintf(stderr, "Cannot receive from server: %s (%d).\n", + net_strerror(nr), + nr); + } + } + if ((r = lock())) { goto end; } + ac_disconnect(connection, false); + if ((r = unlock())) { goto end; } + net_close(&sock); + ac_free(); + net_free(); +#if defined(THREAD_WIN32) + CloseHandle(mtx_conn); +#elif defined(THREAD_C11) + mtx_destroy(&mtx_conn); +#endif + return r; +} \ No newline at end of file -- cgit v1.2.3