diff --git a/.gitignore b/.gitignore index e733775..9237432 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ obj/* obcb obcb-nbd.so live +bot +live-1chunk diff --git a/Makefile b/Makefile index 3a9e5fd..9df5c5f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CFLAGS += LDFLAGS := -lm -OBJECTS := +OBJECTS := obj/obcb.o all: obcb obcb-nbd.so live @@ -10,17 +10,29 @@ test: obcb live-run: live ./live -live: ./src/live.c +live-1chunk-run: live-1chunk + ./live-1chunk + +bot-run: bot + ./bot + +bot: ./src/bot.c $(OBJECTS) + $(CC) $(CFLAGS) $(OBJECTS) src/bot.c $(LDFLAGS) -o bot -lmongoose -lraylib -lm + +live: ./src/live.c $(OBJECTS) $(CC) $(CFLAGS) $(OBJECTS) src/live.c $(LDFLAGS) -o live -lmongoose -lraylib -lm -obcb: lib +live-1chunk: ./src/live-1chunk.c $(OBJECTS) + $(CC) $(CFLAGS) $(OBJECTS) src/live-1chunk.c $(LDFLAGS) -o live-1chunk -lmongoose -lraylib -lm + +obcb: lib $(OBJECTS) $(CC) $(CFLAGS) $(OBJECTS) src/main.c $(LDFLAGS) -o obcb -lmongoose nbd: obcb-nbd.so nbdkit --filter=blocksize-policy -fv ./obcb-nbd.so blocksize-error-policy=error -obcb-nbd.so: src/obcb-nbd.c - $(CC) -ggdb -fPIC -shared src/obcb-nbd.c -o obcb-nbd.so -lmongoose `pkg-config nbdkit --cflags --libs` +obcb-nbd.so: src/obcb-nbd.c $(OBJECTS) + $(CC) $(CFLAGS) $(OBJECTS) -fPIC -shared src/obcb-nbd.c -o obcb-nbd.so -lmongoose `pkg-config nbdkit --cflags --libs` lib: $(OBJECTS) diff --git a/chunk420.data b/chunk420.data new file mode 100644 index 0000000..1a1b51b Binary files /dev/null and b/chunk420.data differ diff --git a/chunk420.data.pal b/chunk420.data.pal new file mode 100644 index 0000000..c34a7f3 Binary files /dev/null and b/chunk420.data.pal differ diff --git a/chunk420.png b/chunk420.png new file mode 100644 index 0000000..4e29c54 Binary files /dev/null and b/chunk420.png differ diff --git a/src/bot.c b/src/bot.c new file mode 100644 index 0000000..e67446d --- /dev/null +++ b/src/bot.c @@ -0,0 +1,129 @@ +// x-run: make -C.. bot-run +#include +#include +#include +#include "obcb.h" +#include "packets.h" + +unsigned char chunk_current[OBCB_CHUNK_SIZE_BYTES], chunk_target[OBCB_CHUNK_SIZE_BYTES]; + +int dst_chunk_id = 420; + +const uint8_t shuffle[256] = { + 0, 128, 64, 192, 32, 160, 96, 224, 16, + 144, 80, 208, 48, 176, 112, 240, 8, + 136, 72, 200, 40, 168, 104, 232, 24, + 152, 88, 216, 56, 184, 120, 248, 4, + 132, 68, 196, 36, 164, 100, 228, 20, + 148, 84, 212, 52, 180, 116, 244, 12, + 140, 76, 204, 44, 172, 108, 236, 28, + 156, 92, 220, 60, 188, 124, 252, 2, + 130, 66, 194, 34, 162, 98, 226, 18, + 146, 82, 210, 50, 178, 114, 242, 10, + 138, 74, 202, 42, 170, 106, 234, 26, + 154, 90, 218, 58, 186, 122, 250, 6, + 134, 70, 198, 38, 166, 102, 230, 22, + 150, 86, 214, 54, 182, 118, 246, 14, + 142, 78, 206, 46, 174, 110, 238, 30, + 158, 94, 222, 62, 190, 126, 254, 1, + 129, 65, 193, 33, 161, 97, 225, 17, + 145, 81, 209, 49, 177, 113, 241, 9, + 137, 73, 201, 41, 169, 105, 233, 25, + 153, 89, 217, 57, 185, 121, 249, 5, + 133, 69, 197, 37, 165, 101, 229, 21, + 149, 85, 213, 53, 181, 117, 245, 13, + 141, 77, 205, 45, 173, 109, 237, 29, + 157, 93, 221, 61, 189, 125, 253, 3, + 131, 67, 195, 35, 163, 99, 227, 19, + 147, 83, 211, 51, 179, 115, 243, 11, + 139, 75, 203, 43, 171, 107, 235, 27, + 155, 91, 219, 59, 187, 123, 251, 7, + 135, 71, 199, 39, 167, 103, 231, 23, + 151, 87, 215, 55, 183, 119, 247, 15, + 143, 79, 207, 47, 175, 111, 239, 31, + 159, 95, 223, 63, 191, 127, 255 +}; + +static void obcb_mg_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_OPEN) { + printf("Connected to WebSocket\n"); + } else if (ev == MG_EV_WS_OPEN) { + obcb_send_full_state_request(c, 0); + } else if (ev == MG_EV_WS_MSG) { + struct mg_ws_message *wsm = (struct mg_ws_message*)ev_data; + if (wsm->data.buf[0] == OBCB_MSG_HELLO) { + struct obcb_msg_hello hello; memcpy(&hello, &wsm->data.buf[1], sizeof(hello)); + printf("HELLO: v%d.%d. Why hello there, honey~\n", hello.version_major, hello.version_minor); + } else if (wsm->data.buf[0] == OBCB_MSG_STATS) { + struct obcb_msg_stats stats; memcpy(&stats, &wsm->data.buf[1], sizeof(stats)); + } else if (wsm->data.buf[0] == OBCB_MSG_FULL_STATE_RESPONSE) { + struct obcb_msg_full_state_response state; memcpy(&state, &wsm->data.buf[1], sizeof(state)); + if (state.chunk_index == dst_chunk_id) { + memcpy(chunk_current, state.bitmap, OBCB_CHUNK_SIZE_BYTES); + } + printf("\n"); + } else if (wsm->data.buf[0] == OBCB_MSG_PARTIAL_STATE_UPDATE) { + struct obcb_msg_partial_state_update update; memcpy(&update, &wsm->data.buf[1], sizeof(update)); + uint32_t chunk_index = update.offset / OBCB_CHUNK_SIZE_BYTES; + uint32_t byte_index = update.offset % OBCB_CHUNK_SIZE_BYTES; + if (chunk_index == dst_chunk_id) { + memcpy(&chunk_current[byte_index], update.chunk, OBCB_UPDATE_CHUNK_SIZE); + } + } else { + printf("UNHANDLED MESSAGE 0x%02x\n", wsm->data.buf[0]); + } + } +} + +int render_thread(void *_conn); + +int main(void) { + + { + static unsigned char line[512]; + FILE *fp = fopen("./chunk420.data", "rb"); + for (int y = 0; y < 512; y++) { + fread(line, 512, 1, fp); + for (int x = 0; x < 512; x++) { + chunk_target[(x + y * 512) / 8] |= line[x] << (x & 7); + } + } + fclose(fp); + } + + struct mg_mgr manager; + mg_mgr_init(&manager); + + struct mg_connection *ws = mg_ws_connect(&manager, "ws://bitmap-ws.alula.me/", obcb_mg_handler, 0, "%s", "User-Agent: maidcore/4.2.0 (kc.is.being.pet)\r\n"); + + obcb_send_full_state_request(ws, dst_chunk_id); + obcb_send_partial_subscription(ws, dst_chunk_id); + + thrd_t render_thrd; + thrd_create(&render_thrd, render_thread, ws); + + while (1) mg_mgr_poll(&manager, 1000); + mg_mgr_free(&manager); + return 0; +} + +int render_thread(void *_conn) { + struct mg_connection *ws = _conn; + + for (int i = 0; true; i = (i + 1) % (512 * 512)) { + int blk_index = i / 256; + int blk_pos = shuffle[i % 256]; + int ox = blk_pos & 15, oy = blk_pos >> 4; + int x = (blk_index & 31) + ox * 32; + int y = (blk_index >> 5) + oy * 32; + + int index = x + y * 512; + int byte = index >> 3, bit = index & 7; + int mask = 1 << bit; + + if ((chunk_current[byte] & mask) != (chunk_target[byte] & mask)) { + obcb_send_toggle_bit(ws, index + dst_chunk_id * OBCB_CHUNK_SIZE); + usleep(5000); + } + } +} diff --git a/src/live-1chunk.c b/src/live-1chunk.c new file mode 100644 index 0000000..6032283 --- /dev/null +++ b/src/live-1chunk.c @@ -0,0 +1,96 @@ +// x-run: make -C.. CFLAGS+=-ggdb live-1chunk-run +#include +#include +#include +#include +#include "obcb.h" +#include "packets.h" + +struct mg_mgr manager; +struct mg_connection *ws = NULL; + +int chunk_id = 420; +int connected_clients = 0; + +static void obcb_mg_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_OPEN) { + printf("Connected to WebSocket\n"); + c->is_hexdumping = 0; + } else if (ev == MG_EV_WS_MSG) { + struct mg_ws_message *wsm = (struct mg_ws_message*)ev_data; + if (wsm->data.buf[0] == OBCB_MSG_HELLO) { + struct obcb_msg_hello hello; memcpy(&hello, &wsm->data.buf[1], sizeof(hello)); + printf("HELLO: v%d.%d. Why hello there, honey~\n", hello.version_major, hello.version_minor); + } else if (wsm->data.buf[0] == OBCB_MSG_STATS) { + struct obcb_msg_stats stats; memcpy(&stats, &wsm->data.buf[1], sizeof(stats)); + printf("STATS: %d clients connected\n", stats.current_clients); + connected_clients = stats.current_clients; + } else if (wsm->data.buf[0] == OBCB_MSG_FULL_STATE_RESPONSE) { + struct obcb_msg_full_state_response state; memcpy(&state, &wsm->data.buf[1], sizeof(state)); + if (state.chunk_index != chunk_id) return; + + unsigned char *view = c->fn_data; + printf("Got full state\n"); + for (int i = 0; i < OBCB_CHUNK_SIZE; i++) { + view[i] = state.bitmap[i >> 3] & (1 << (i & 7)) ? 0xff : 0x00; + } + } else if (wsm->data.buf[0] == OBCB_MSG_PARTIAL_STATE_UPDATE) { + struct obcb_msg_partial_state_update update; memcpy(&update, &wsm->data.buf[1], sizeof(update)); + uint32_t chunk_index = update.offset / OBCB_CHUNK_SIZE_BYTES; + uint32_t byte_offset = update.offset % OBCB_CHUNK_SIZE_BYTES; + + if (chunk_index != chunk_id) return; + + printf("Got partial update: %d %d\n", chunk_index, byte_offset); + unsigned char *view = c->fn_data; + for (int i = 0; i < OBCB_UPDATE_CHUNK_SIZE; i++) { + view[i + byte_offset * 8] = update.chunk[i >> 3] & (1 << (i & 7)) ? 0xFF : 0x00; + } + } else { + printf("UNHANDLED MESSAGE 0x%02x\n", wsm->data.buf[0]); + } + } +} + +static int mongoose_thread(void *_mgr) { + mg_mgr_init(&manager); + while (true) mg_mgr_poll(&manager, 1000); + mg_mgr_free(&manager); + printf("Manager exited\n"); + return 0; +} + +int main(void) { + InitWindow(512, 512, "obcb-1chunk"); + SetTargetFPS(60); + + thrd_t mongoose_manager_thrd; + thrd_create(&mongoose_manager_thrd, mongoose_thread, 0); + + Image img = { + .data = MemAlloc(512 * 512), + .width = 512, + .height = 512, + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE + }; + + Texture2D tex = LoadTextureFromImage(img); + + struct mg_connection *ws = mg_ws_connect(&manager, "ws://bitmap-ws.alula.me/", obcb_mg_handler, img.data, "%s", "maidcore/4.2.0 (kc.is.being.pet)\r\n"); + if (!ws) return 1; + + obcb_send_full_state_request(ws, chunk_id); + obcb_send_partial_subscription(ws, chunk_id); + + for (int frame = 0; !WindowShouldClose(); frame++) { + if ((frame % 512) == 511) { + printf("Requesting state again\n"); + obcb_send_full_state_request(ws, chunk_id); + } + BeginDrawing(); + UpdateTexture(tex, img.data); + DrawTexture(tex, 0, 0, WHITE); + EndDrawing(); + } +} diff --git a/src/live.c b/src/live.c index 1b47b67..690af86 100644 --- a/src/live.c +++ b/src/live.c @@ -5,12 +5,10 @@ #include #include #include -#include "packets.h" +#include "obcb.h" #define WAITING_QUEUE_SIZE 32 -static void obcb_send_full_state_request(struct mg_connection *c, uint16_t index); - static uint32_t connected_clients = 0; static bool running = true; @@ -61,7 +59,7 @@ struct img_and_tex { Texture2D tex; } blocks[32 * 32], subscribed; -int subscribed_chunk_id = -1; +int subscribed_chunk_id = 420; static struct waiting_queue_elem { uint16_t index; @@ -176,14 +174,8 @@ int main(void) { thrd_create(&mongoose_manager_thrd, mongoose_thread, &manager); if (subscribed_chunk_id >= 0) { - static unsigned char packet[sizeof(struct obcb_msg_partial_state_subscription) + 1]; - packet[0] = OBCB_MSG_PARTIAL_STATE_SUBSCRIPTION; - struct obcb_msg_partial_state_subscription *msg = (struct obcb_msg_partial_state_subscription *)&packet[1]; - msg->chunk_index = subscribed_chunk_id; - + obcb_send_partial_subscription(ws, subscribed_chunk_id); memset(subscribed.img.data, 0x55, 512 * 512); - - mg_ws_send(ws, packet, sizeof(packet), WEBSOCKET_OP_BINARY); } Font font = LoadFontEx("/home/kc/.local/share/fonts/DepartureMono-Regular.otf", 14, 0, 1024); @@ -318,30 +310,18 @@ int main(void) { if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { if (IsKeyDown(KEY_LEFT_SHIFT)) { - static unsigned char packet[1] = { OBCB_MSG_PARTIAL_STATE_UNSUBSCRIPTION }; - mg_ws_send(ws, packet, sizeof(packet), WEBSOCKET_OP_BINARY); + obcb_send_partial_unsubscription(ws); subscribed_chunk_id = -1; } else { - static unsigned char packet[sizeof(struct obcb_msg_partial_state_subscription) + 1]; - packet[0] = OBCB_MSG_PARTIAL_STATE_SUBSCRIPTION; - struct obcb_msg_partial_state_subscription *msg = (struct obcb_msg_partial_state_subscription *)&packet[1]; - msg->chunk_index = chunk; - subscribed_chunk_id = chunk; - memset(subscribed.img.data, 0x55, 512 * 512); - - mg_ws_send(ws, packet, sizeof(packet), WEBSOCKET_OP_BINARY); + subscribed_chunk_id = chunk; + obcb_send_partial_subscription(ws, chunk); obcb_send_full_state_request(ws, chunk); } } if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - static unsigned char packet[sizeof(struct obcb_msg_toggle_bit) + 1]; - packet[0] = OBCB_MSG_TOGGLE_BIT; - struct obcb_msg_toggle_bit *msg = (struct obcb_msg_toggle_bit *)&packet[1]; - msg->offset = index; - - mg_ws_send(ws, packet, sizeof(packet), WEBSOCKET_OP_BINARY); + obcb_send_toggle_bit(ws, index); } const char *cursor_str = TextFormat( @@ -380,14 +360,6 @@ int main(void) { return 0; } -static void obcb_send_full_state_request(struct mg_connection *c, uint16_t index) { - static unsigned char packet[sizeof(struct obcb_msg_full_state_request) + 1]; - struct obcb_msg_full_state_request *msg = (struct obcb_msg_full_state_request *)&packet[1]; - packet[0] = OBCB_MSG_FULL_STATE_REQUEST; - msg->chunk_index = index; - mg_ws_send(c, packet, sizeof(packet), WEBSOCKET_OP_BINARY); -} - void DrawTextShadow(Font font, const char *txt, int x, int y, int size, Color color) { Vector2 pos = { x, y }; for (int oy = -2; oy <= 2; oy++) { diff --git a/src/obcb.c b/src/obcb.c new file mode 100644 index 0000000..9fb39c9 --- /dev/null +++ b/src/obcb.c @@ -0,0 +1,33 @@ +#include "obcb.h" +#include + +void obcb_send_full_state_request(struct mg_connection *c, uint16_t index) { + static unsigned char packet[sizeof(struct obcb_msg_full_state_request) + 1]; + struct obcb_msg_full_state_request *msg = (struct obcb_msg_full_state_request *)&packet[1]; + packet[0] = OBCB_MSG_FULL_STATE_REQUEST; + msg->chunk_index = index; + mg_ws_send(c, packet, sizeof(packet), WEBSOCKET_OP_BINARY); +} + +void obcb_send_toggle_bit(struct mg_connection *c, uint32_t offset) { + static unsigned char packet[sizeof(struct obcb_msg_toggle_bit) + 1]; + packet[0] = OBCB_MSG_TOGGLE_BIT; + struct obcb_msg_toggle_bit *msg = (struct obcb_msg_toggle_bit *)&packet[1]; + msg->offset = offset; + + mg_ws_send(c, packet, sizeof(packet), WEBSOCKET_OP_BINARY); +} + +void obcb_send_partial_subscription(struct mg_connection *c, uint16_t chunk) { + static unsigned char packet[sizeof(struct obcb_msg_partial_state_subscription) + 1]; + packet[0] = OBCB_MSG_PARTIAL_STATE_SUBSCRIPTION; + struct obcb_msg_partial_state_subscription *msg = (struct obcb_msg_partial_state_subscription *)&packet[1]; + msg->chunk_index = chunk; + + mg_ws_send(c, packet, sizeof(packet), WEBSOCKET_OP_BINARY); +} + +void obcb_send_partial_unsubscription(struct mg_connection *c) { + static unsigned char packet[1] = { OBCB_MSG_PARTIAL_STATE_UNSUBSCRIPTION }; + mg_ws_send(c, packet, sizeof(packet), WEBSOCKET_OP_BINARY); +} diff --git a/src/obcb.h b/src/obcb.h new file mode 100644 index 0000000..f07b120 --- /dev/null +++ b/src/obcb.h @@ -0,0 +1,12 @@ +#ifndef _OBCB_H_ +#define _OBCB_H_ + +#include "packets.h" +#include + +void obcb_send_full_state_request(struct mg_connection *c, uint16_t index); +void obcb_send_toggle_bit(struct mg_connection *c, uint32_t offset); +void obcb_send_partial_subscription(struct mg_connection *c, uint16_t chunk); +void obcb_send_partial_unsubscription(struct mg_connection *c); + +#endif