#include #include #include #include #include #include #include #include "dp_common.h" typedef struct DoomContext DoomContext; #define DEF_DOOM_STATE_FUNC(fn) int fn(DoomContext *ctx) typedef DEF_DOOM_STATE_FUNC(DoomStateFunc); typedef struct DoomContext { nng_socket pub; nng_socket sub; doomid_t id; DP_DoomState state; DoomStateFunc *f; } DoomContext; DEF_DOOM_STATE_FUNC(do_doom_ready); DEF_DOOM_STATE_FUNC(do_doom_running); DEF_DOOM_STATE_FUNC(do_doom_ready) { assert(ctx->state == DP_DS_Ready); int res = 0; // Publish { DP_MT_DoomReady, doomid }. { nng_msg *msg = NULL; if ((res = nng_msg_alloc(&msg, 0))) dp_nng_fatal("doom/alloc", res); if ((res = nng_msg_append_u16(msg, DP_MT_DoomReady))) dp_nng_fatal("doom/msg", res); if ((res = nng_msg_append_u32(msg, ctx->id))) dp_nng_fatal("doom/msg", res); if ((res = nng_sendmsg(ctx->pub, msg, 0))) dp_nng_fatal("doom/sendmsg", res); } // Incoming Message | Next State // ------------------------------- // DP_MT_RunDoom -> DP_DS_Running // DP_MT_QuitDoom -> DP_DS_Quitting // */none -> DP_DS_Ready { nng_msg *msg = NULL; if ((res = dp_recv_new_msg(ctx->sub, &msg))) { if (res != NNG_ETIMEDOUT) dp_nng_fatal("doom/recvmsg", res); return 0; } auto len = nng_msg_len(msg); log_trace("do_doom_ready received message of size %zu", len); dmt_t mt = DP_MT_Invalid; if ((res = nng_msg_trim_u16(msg, &mt))) dp_nng_fatal("doom/msg_trim", res); nng_msg_free(msg); if (mt == DP_MT_RunDoom) { ctx->state = DP_DS_Running; ctx->f = do_doom_running; } else if (mt == DP_MT_QuitDoom) // TODO: pop off the doomid and check against ours. // TODO: add a QuitAllDooms message ctx->state = DP_DS_Quitting; // No error on receiving different message types because other dooms // might get readied or be told to quit. } return res; } DEF_DOOM_STATE_FUNC(do_doom_running) { assert(ctx->state == DP_DS_Running); // Non-blocking receive of incoming messages. // Check if we should quit. // Handle incoming input. // Let doom render a new frame. // Publish the frame. int res = 0; nng_msg *msg = NULL; // FIXME: this is blocking while testing things if ((res = dp_recv_new_msg_nonblock(ctx->sub, &msg))) { if (res != NNG_ETIMEDOUT && res != NNG_EAGAIN) dp_nng_fatal("doom/recvmsg", res); return 0; } auto len = nng_msg_len(msg); log_trace("do_doom_running received message of size %zu", len); dmt_t mt = DP_MT_Invalid; if ((res = nng_msg_trim_u16(msg, &mt))) dp_nng_fatal("doom/msg_trim", res); nng_msg_free(msg); if (mt == DP_MT_QuitDoom) ctx->state = DP_DS_Quitting; // Otherwise stay in running state return 0; } int doom_loop(DoomContext *ctx) { log_debug("doom#%zu started", ctx->id); int res = 0; while (ctx->state != DP_DS_Quitting && res == 0) { DP_DoomState prevState = ctx->state; res = ctx->f(ctx); if (prevState != ctx->state) { log_info("transition %s -> %s", doomstate_str(prevState), doomstate_str(ctx->state)); } } return res; } int main(int argc, char *argv[]) { #ifndef NDEBUG log_set_level(LOG_TRACE); #else log_set_level(LOG_DEBUG); #endif log_set_level(LOG_DEBUG); // FIXME: silence! log_info("doomsim starting"); std::optional doomId; for (int i=1; i parameter!"); return 1; } dp_nng_init_limits(1, 1, 1); auto pubSock = make_doom_pub(DoomUrl); auto subSock = make_doom_sub(CtrlUrl); DoomContext ctx { pubSock, subSock, *doomId, DP_DS_Ready, do_doom_ready, }; int ret = doom_loop(&ctx); nng_close(pubSock); nng_close(subSock); return ret; }