Live viewer, attempts at nbdkit stuff, etc
This commit is contained in:
parent
3dd8217afe
commit
f56cdb95f4
|
@ -1,2 +1,4 @@
|
|||
obj/*
|
||||
obcb
|
||||
obcb-nbd.so
|
||||
live
|
||||
|
|
21
Makefile
21
Makefile
|
@ -2,10 +2,25 @@ CFLAGS +=
|
|||
LDFLAGS := -lm
|
||||
OBJECTS :=
|
||||
|
||||
obcb: lib
|
||||
$(CC) $(CFLAGS) $(OBJECTS) src/main.c $(LDFLAGS) -o obcb
|
||||
all: obcb obcb-nbd.so live
|
||||
|
||||
all: obcb
|
||||
test: obcb
|
||||
./obcb
|
||||
|
||||
live-run: live
|
||||
./live
|
||||
|
||||
live: ./src/live.c
|
||||
$(CC) $(CFLAGS) $(OBJECTS) src/live.c $(LDFLAGS) -o live -lmongoose -lraylib -lm
|
||||
|
||||
obcb: lib
|
||||
$(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`
|
||||
|
||||
lib: $(OBJECTS)
|
||||
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// x-run: make -C.. live-run
|
||||
#include <raylib.h>
|
||||
#include <raymath.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <mongoose.h>
|
||||
#include <threads.h>
|
||||
#include "packets.h"
|
||||
|
||||
static void obcb_send_full_state_request(struct mg_connection *c, uint16_t index);
|
||||
|
||||
static uint32_t connected_clients = 0;
|
||||
static bool running = true;
|
||||
|
||||
struct mg_mgr manager;
|
||||
|
||||
struct chunk {
|
||||
double last_update;
|
||||
double last_request;
|
||||
} chunks[OBCB_CHUNK_COUNT];
|
||||
|
||||
struct img_and_tex {
|
||||
Image img;
|
||||
Texture2D tex;
|
||||
} blocks[32 * 32];
|
||||
|
||||
|
||||
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_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));
|
||||
chunks[state.chunk_index].last_update = GetTime();
|
||||
for (int i = 0; i < OBCB_CHUNK_SIZE; i++) {
|
||||
int index = i + state.chunk_index * OBCB_CHUNK_SIZE;
|
||||
int x = index % 32768, y = index / 32768;
|
||||
int chunk_x = x / 1024, chunk_y = y / 1024;
|
||||
int in_chunk_x = x % 1024, in_chunk_y = y % 1024;
|
||||
uint8_t *img = blocks[chunk_x + chunk_y * 32].img.data;
|
||||
img[in_chunk_x + in_chunk_y * 1024] = state.bitmap[i >> 3] & (0x80 >> (i & 7)) ? 0xFF : 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mongoose_thread(void *_mgr) {
|
||||
while (running) mg_mgr_poll((struct mg_mgr *)_mgr, 1000);
|
||||
mg_mgr_free((struct mg_mgr *)_mgr);
|
||||
return 0;
|
||||
}
|
||||
static unsigned char rendered_canvas[OBCB_BITMAP_SIZE];
|
||||
|
||||
int main(void) {
|
||||
mg_mgr_init(&manager);
|
||||
struct mg_connection *ws = mg_ws_connect(&manager, "ws://bitmap-ws.alula.me/", obcb_mg_handler, 0, 0);
|
||||
|
||||
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
||||
SetTargetFPS(60);
|
||||
InitWindow(800, 600, "obcb");
|
||||
|
||||
thrd_t mongoose_manager_thrd;
|
||||
thrd_create(&mongoose_manager_thrd, mongoose_thread, &manager);
|
||||
|
||||
for (int i = 0; i < 32 * 32; i++) {
|
||||
blocks[i].img.data = &rendered_canvas[i * 1024 * 1024];
|
||||
blocks[i].img.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
|
||||
blocks[i].img.width = 1024;
|
||||
blocks[i].img.height = 1024;
|
||||
blocks[i].img.mipmaps = 1;
|
||||
blocks[i].tex = LoadTextureFromImage(blocks[i].img);
|
||||
SetTextureFilter(blocks[i].tex, TEXTURE_FILTER_ANISOTROPIC_16X);
|
||||
}
|
||||
|
||||
Camera2D cam = {
|
||||
.offset.x = 400,
|
||||
.offset.y = 300,
|
||||
.target.x = 0,
|
||||
.target.y = 0,
|
||||
.rotation = 0,
|
||||
.zoom = 0.125
|
||||
};
|
||||
|
||||
double zoom = 0.5;
|
||||
for (unsigned long frame = 0; !WindowShouldClose(); frame++) {
|
||||
double current_time = GetTime();
|
||||
if (IsWindowResized()) {
|
||||
cam.offset.x = (int)(GetScreenWidth() / 2.0);
|
||||
cam.offset.y = (int)(GetScreenHeight() / 2.0);
|
||||
}
|
||||
|
||||
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
|
||||
cam.target = Vector2Subtract(cam.target, Vector2Scale(GetMouseDelta(), 1.0 / cam.zoom));
|
||||
}
|
||||
|
||||
zoom += GetMouseWheelMove() * 0.01;
|
||||
cam.zoom = pow(zoom, 4.0);
|
||||
|
||||
{
|
||||
|
||||
int limit = 32;
|
||||
for (int i = 0; i < OBCB_CHUNK_COUNT; i++) {
|
||||
if (((current_time - chunks[i].last_update) > 5 || (current_time - chunks[i].last_request) > 30) && limit > 0) {
|
||||
chunks[i].last_request = current_time;
|
||||
chunks[i].last_update = current_time;
|
||||
obcb_send_full_state_request(ws, i);
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
struct img_and_tex *block = &blocks[i + (frame % 32) * 32];
|
||||
UpdateTexture(block->tex, block->img.data);
|
||||
}
|
||||
}
|
||||
|
||||
BeginDrawing();
|
||||
{
|
||||
ClearBackground(BLACK);
|
||||
BeginMode2D(cam);
|
||||
{
|
||||
DrawRectangle(-32, -32, 32832, 32832, RED);
|
||||
for (int cy = 0; cy < 32; cy++) {
|
||||
for (int cx = 0; cx < 32; cx++) {
|
||||
if (blocks[cx + cy * 32].tex.id != 0) {
|
||||
DrawTexture(blocks[cx + cy * 32].tex, cx * 1024, cy * 1024, WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EndMode2D();
|
||||
|
||||
{
|
||||
for (int cy = 0; cy < 64; cy++) {
|
||||
for (int cx = 0; cx < 64; cx++) {
|
||||
double since_last_update = current_time - chunks[cx + cy * 64].last_update;
|
||||
double since_last_request = current_time - chunks[cx + cy * 64].last_request;
|
||||
DrawRectangle(cx * 2, cy * 2 + 128, 2, 2, (Color){
|
||||
.r = 255 * (5.0 - since_last_update) / 5.0,
|
||||
.g = 255 * (10.0 - since_last_request) / 10.0,
|
||||
.b = 0,
|
||||
.a = 255
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawFPS(8, 8);
|
||||
DrawText(TextFormat("Clients: %d", connected_clients), 8, 28, 20, WHITE);
|
||||
DrawText(TextFormat("Camera: %.2f %.2f (1/%.2f) offset:[%.2f %.2f]",
|
||||
cam.target.x, cam.target.y, 1.0 / cam.zoom,
|
||||
cam.offset.x, cam.offset.y), 8, 48, 20, WHITE);
|
||||
}
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
47
src/main.c
47
src/main.c
|
@ -1,6 +1,49 @@
|
|||
// x-run: make -C.. test
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <mongoose.h>
|
||||
#include "packets.h"
|
||||
|
||||
static void obcb_send_full_state_request(struct mg_connection *c, uint16_t index);
|
||||
|
||||
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));
|
||||
/*stats.current_clients = htonl(stats.current_clients);*/
|
||||
printf("STATS: %d clients connected\n", 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));
|
||||
printf("STATE[%d] =", state.chunk_index);
|
||||
for (int i = 0; i < sizeof(state.bitmap); i++) {
|
||||
printf(" %02x", state.bitmap[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("Hi!\n");
|
||||
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, 0);
|
||||
while (1) mg_mgr_poll(&manager, 1000);
|
||||
mg_mgr_free(&manager);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <mongoose.h>
|
||||
#include <string.h>
|
||||
#include <threads.h>
|
||||
|
||||
#define NBDKIT_API_VERSION 2
|
||||
#include <nbdkit-plugin.h>
|
||||
|
||||
#include "packets.h"
|
||||
|
||||
#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS
|
||||
|
||||
struct mg_mgr mg_manager;
|
||||
thrd_t manager_thread;
|
||||
struct mg_connection *obcb_mg_conn;
|
||||
|
||||
static void obcb_nbd_mg_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_OPEN) {
|
||||
c->is_hexdumping = 1;
|
||||
nbdkit_debug("Connected to WebSocket");
|
||||
} 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, sizeof(hello));
|
||||
nbdkit_debug("HELLO: v%d.%d. Why hello there, honey", 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, sizeof(stats));
|
||||
nbdkit_debug("STATS: %d clients connected", stats.current_clients);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int obcb_nbd_mg_thread(void *param) {
|
||||
nbdkit_debug("Running in a thread now...");
|
||||
obcb_mg_conn = mg_ws_connect(&mg_manager, "wss://bitmap-ws.alula.me/", obcb_nbd_mg_handler, 0, 0);
|
||||
while (1) mg_mgr_poll(&mg_manager, 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void obcb_nbd_load(void) {
|
||||
nbdkit_debug("Hello from OBCB!");
|
||||
}
|
||||
|
||||
static int obcb_nbd_after_fork(void) {
|
||||
mg_mgr_init(&mg_manager);
|
||||
// thrd_create(&manager_thread, obcb_nbd_mg_thread, 0);
|
||||
obcb_nbd_mg_thread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *obcb_nbd_open(int readonly) {
|
||||
return NBDKIT_HANDLE_NOT_NEEDED;
|
||||
}
|
||||
|
||||
static int64_t obcb_nbd_get_size(void *handle) {
|
||||
return OBCB_BITMAP_SIZE / 8;
|
||||
}
|
||||
|
||||
static int obcb_nbd_block_size(void *handle, uint32_t *minimum, uint32_t *preferred, uint32_t *maximum) {
|
||||
*minimum = OBCB_CHUNK_SIZE_BYTES;
|
||||
*preferred = OBCB_CHUNK_SIZE_BYTES;
|
||||
*maximum = OBCB_CHUNK_SIZE_BYTES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int obcb_nbd_pread(void *_handle, void *buf, uint32_t count, uint64_t offset, uint32_t flags) {
|
||||
if (count != OBCB_CHUNK_SIZE_BYTES || offset % OBCB_CHUNK_SIZE_BYTES) {
|
||||
nbdkit_error("Invalid block size (should be %d)", OBCB_CHUNK_SIZE_BYTES);
|
||||
return -1;
|
||||
}
|
||||
nbdkit_debug("READ at %ld of %d", offset, count);
|
||||
memset(buf, 0xAA, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void obcb_nbd_close(void *_handle) {
|
||||
}
|
||||
|
||||
static struct nbdkit_plugin plugin = {
|
||||
.name = "obcb",
|
||||
.longname = "One Billion Checkboxes",
|
||||
.description = "Using One Billion Checkboxes as a storage",
|
||||
.load = obcb_nbd_load,
|
||||
.open = obcb_nbd_open,
|
||||
.get_size = obcb_nbd_get_size,
|
||||
.block_size = obcb_nbd_block_size,
|
||||
.pread = obcb_nbd_pread
|
||||
};
|
||||
|
||||
NBDKIT_REGISTER_PLUGIN(plugin);
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef _PACKETS_H_
|
||||
#define _PACKETS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// The size of a single chunk in bits
|
||||
#define OBCB_CHUNK_SIZE (64 * 64 * 64)
|
||||
|
||||
// The size of a single chunk in bytes
|
||||
#define OBCB_CHUNK_SIZE_BYTES (OBCB_CHUNK_SIZE / 8)
|
||||
|
||||
// The number of chunks
|
||||
#define OBCB_CHUNK_COUNT (64 * 64)
|
||||
|
||||
// The size of the entire bitmap in bits
|
||||
#define OBCB_BITMAP_SIZE (OBCB_CHUNK_SIZE * OBCB_CHUNK_COUNT)
|
||||
|
||||
// The size of a single update chunk in bytes
|
||||
#define OBCB_UPDATE_CHUNK_SIZE (32)
|
||||
|
||||
enum obcb_message_e {
|
||||
OBCB_MSG_HELLO = 0x00,
|
||||
OBCB_MSG_STATS = 0x01,
|
||||
OBCB_MSG_FULL_STATE_REQUEST = 0x10,
|
||||
OBCB_MSG_FULL_STATE_RESPONSE = 0x11,
|
||||
OBCB_MSG_PARTIAL_STATE_UPDATE = 0x12,
|
||||
OBCB_MSG_TOGGLE_BIT = 0x13,
|
||||
OBCB_MSG_PARTIAL_STATE_SUBSCRIPTION = 0x14,
|
||||
OBCB_MSG_PARTIAL_STATE_UNSUBSCRIPTION = 0x15,
|
||||
};
|
||||
|
||||
#define OBCB_MSG(NAME, SIDE) __attribute__((__packed__)) obcb_msg_##NAME
|
||||
|
||||
struct OBCB_MSG(hello, SERVER) {
|
||||
uint16_t version_major;
|
||||
uint16_t version_minor;
|
||||
};
|
||||
|
||||
struct OBCB_MSG(stats, SERVER) {
|
||||
uint32_t current_clients;
|
||||
uint8_t reserved[60];
|
||||
};
|
||||
|
||||
struct OBCB_MSG(full_state_request, CLIENT) {
|
||||
uint16_t chunk_index;
|
||||
};
|
||||
|
||||
struct OBCB_MSG(full_state_response, SERVER) {
|
||||
uint16_t chunk_index;
|
||||
uint8_t bitmap[OBCB_CHUNK_SIZE_BYTES];
|
||||
};
|
||||
|
||||
struct OBCB_MSG(partial_state_update, SERVER) {
|
||||
uint16_t offset; // in bytes, global
|
||||
uint8_t chunk[OBCB_UPDATE_CHUNK_SIZE];
|
||||
};
|
||||
|
||||
struct OBCB_MSG(toggle_bit, CLIENT) {
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct OBCB_MSG(partial_state_subscription, CLIENT) {
|
||||
uint16_t chunk_index;
|
||||
};
|
||||
|
||||
struct OBCB_MSG(partial_state_unsubscription, CLIENT) {
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue