Made live viewer work...?
This commit is contained in:
parent
f56cdb95f4
commit
7222d4cc8f
280
src/live.c
280
src/live.c
|
@ -1,4 +1,4 @@
|
|||
// x-run: make -C.. live-run
|
||||
// x-run: make -C.. CFLAGS+=-ggdb live-run
|
||||
#include <raylib.h>
|
||||
#include <raymath.h>
|
||||
#include <stdint.h>
|
||||
|
@ -7,23 +7,68 @@
|
|||
#include <threads.h>
|
||||
#include "packets.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;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct mg_mgr manager;
|
||||
|
||||
struct chunk {
|
||||
double last_update;
|
||||
double last_request;
|
||||
bool waiting;
|
||||
} chunks[OBCB_CHUNK_COUNT];
|
||||
|
||||
struct img_and_tex {
|
||||
Image img;
|
||||
Texture2D tex;
|
||||
} blocks[32 * 32];
|
||||
} blocks[32 * 32], subscribed;
|
||||
|
||||
int subscribed_chunk_id = -1;
|
||||
|
||||
static struct waiting_queue_elem {
|
||||
uint16_t index;
|
||||
bool occupied;
|
||||
bool waiting;
|
||||
double waiting_since;
|
||||
} waiting_queue[WAITING_QUEUE_SIZE] = { 0 };
|
||||
|
||||
static void obcb_mg_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_OPEN) {
|
||||
|
@ -40,15 +85,53 @@ static void obcb_mg_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||
} 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();
|
||||
chunks[state.chunk_index].waiting = false;
|
||||
for (int i = 0; i < WAITING_QUEUE_SIZE; i++) {
|
||||
if (waiting_queue[i].index == state.chunk_index) {
|
||||
waiting_queue[i].occupied = false;
|
||||
waiting_queue[i].waiting = false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *view = subscribed.img.data;
|
||||
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;
|
||||
bool bit = state.bitmap[i >> 3] & (1 << (i & 7));
|
||||
img[in_chunk_x + in_chunk_y * 1024] = bit ? 0xFF : 0x00;
|
||||
|
||||
if (state.chunk_index == subscribed_chunk_id) {
|
||||
view[i] = bit ? 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));
|
||||
printf("PARTIAL UPDATE AT %d\n", update.offset);
|
||||
for (int i = 0; i < OBCB_UPDATE_CHUNK_SIZE; i++) {
|
||||
int index = update.offset * 8 + i;
|
||||
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] = update.chunk[i >> 3] & (1 << (i & 7)) ? 0xFF : 0x00;
|
||||
}
|
||||
|
||||
uint32_t chunk_index = update.offset / OBCB_CHUNK_SIZE_BYTES;
|
||||
uint32_t byte_offset = update.offset % OBCB_CHUNK_SIZE_BYTES;
|
||||
if (chunk_index == subscribed_chunk_id) {
|
||||
printf("That's our chunk!\n");
|
||||
unsigned char *view = subscribed.img.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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,22 +140,19 @@ static int mongoose_thread(void *_mgr) {
|
|||
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);
|
||||
void DrawTextShadow(Font font, const char *txt, int x, int y, int size, Color color);
|
||||
|
||||
int main(void) {
|
||||
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.data = &rendered_canvas[i * 1024 * 1024];
|
||||
blocks[i].img.width = 1024;
|
||||
blocks[i].img.height = 1024;
|
||||
blocks[i].img.mipmaps = 1;
|
||||
|
@ -80,6 +160,34 @@ int main(void) {
|
|||
SetTextureFilter(blocks[i].tex, TEXTURE_FILTER_ANISOTROPIC_16X);
|
||||
}
|
||||
|
||||
subscribed.img.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
|
||||
subscribed.img.data = MemAlloc(512 * 512);
|
||||
subscribed.img.width = 512;
|
||||
subscribed.img.height = 512;
|
||||
subscribed.img.mipmaps = 1;
|
||||
|
||||
subscribed.tex = LoadTextureFromImage(subscribed.img);
|
||||
|
||||
mg_mgr_init(&manager);
|
||||
struct mg_connection *ws = mg_ws_connect(&manager, "ws://bitmap-ws.alula.me/", obcb_mg_handler, 0, 0);
|
||||
if (!ws) return 0;
|
||||
|
||||
thrd_t mongoose_manager_thrd;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
Camera2D cam = {
|
||||
.offset.x = 400,
|
||||
.offset.y = 300,
|
||||
|
@ -90,6 +198,8 @@ int main(void) {
|
|||
};
|
||||
|
||||
double zoom = 0.5;
|
||||
|
||||
int req_chunk = 0;
|
||||
for (unsigned long frame = 0; !WindowShouldClose(); frame++) {
|
||||
double current_time = GetTime();
|
||||
if (IsWindowResized()) {
|
||||
|
@ -106,24 +216,58 @@ int main(void) {
|
|||
|
||||
{
|
||||
|
||||
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 < 64; i++) {
|
||||
int index = (req_chunk & ~0xFF) | shuffle[req_chunk & 0xFF];
|
||||
struct chunk *c = &chunks[index];
|
||||
double since_last_update = current_time - c->last_update;
|
||||
|
||||
if (since_last_update >= 5 || c->last_update == 0) {
|
||||
int first_empty = -1;
|
||||
bool already_waiting = false;
|
||||
for (int j = 0; j < WAITING_QUEUE_SIZE; j++) {
|
||||
if (waiting_queue[j].occupied) {
|
||||
if (waiting_queue[j].index == index && waiting_queue[j].waiting) already_waiting = true;
|
||||
continue;
|
||||
}
|
||||
if (first_empty == -1) first_empty = j;
|
||||
}
|
||||
|
||||
if (already_waiting) continue;
|
||||
if (first_empty == -1) continue;
|
||||
|
||||
waiting_queue[first_empty].index = index;
|
||||
waiting_queue[first_empty].occupied = true;
|
||||
waiting_queue[first_empty].waiting_since = current_time;
|
||||
req_chunk = (req_chunk + 1) % OBCB_CHUNK_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (int i = 0; i < WAITING_QUEUE_SIZE; i++) {
|
||||
struct waiting_queue_elem *spot = &waiting_queue[i];
|
||||
if (spot->occupied && !spot->waiting) {
|
||||
obcb_send_full_state_request(ws, spot->index);
|
||||
spot->waiting = true;
|
||||
}
|
||||
|
||||
if (spot->waiting && spot->occupied && (current_time - spot->waiting_since) > 10) {
|
||||
// assume timeout
|
||||
spot->waiting = false;
|
||||
spot->occupied = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (int i = 0; i < 32; i++) {
|
||||
struct img_and_tex *block = &blocks[i + (frame % 32) * 32];
|
||||
UpdateTexture(block->tex, block->img.data);
|
||||
}
|
||||
UpdateTexture(subscribed.tex, subscribed.img.data);
|
||||
}
|
||||
|
||||
BeginDrawing();
|
||||
{
|
||||
Vector2 win_size = { GetScreenWidth(), GetScreenHeight() };
|
||||
ClearBackground(BLACK);
|
||||
BeginMode2D(cam);
|
||||
{
|
||||
|
@ -138,26 +282,97 @@ int main(void) {
|
|||
}
|
||||
EndMode2D();
|
||||
|
||||
{
|
||||
{ // {{{ Rendering chunk map
|
||||
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,
|
||||
.r = 255 * Clamp((5.0 - since_last_update) / 5.0, 0.0, 1.0),
|
||||
.g = 0,
|
||||
.b = chunks[cx + cy * 64].waiting ? 255 : 0,
|
||||
.a = 255
|
||||
});
|
||||
}
|
||||
}
|
||||
} // }}}
|
||||
|
||||
/*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);*/
|
||||
|
||||
{
|
||||
const char *cam_str = TextFormat(
|
||||
"Cam:{%.2f:%.2f,x%.5f}",
|
||||
cam.target.x, cam.target.y, cam.zoom);
|
||||
float x = 8;
|
||||
DrawTextShadow(font, cam_str, x, win_size.y - 8 - 14 * 2, 14, WHITE);
|
||||
x += MeasureTextEx(font, cam_str, 14, 0).x + 16;
|
||||
|
||||
Vector2 cursor_pos = GetMousePosition();
|
||||
Vector2 cursor_world_pos = GetScreenToWorld2D(cursor_pos, cam);
|
||||
if (cursor_world_pos.x >= 0 && cursor_world_pos.y >= 0 && cursor_world_pos.x < 32768 && cursor_world_pos.y < 32768) {
|
||||
int cur_x = (int)cursor_world_pos.x, cur_y = (int)cursor_world_pos.y;
|
||||
int index = cur_x + cur_y * 32768;
|
||||
int chunk = index / OBCB_CHUNK_SIZE;
|
||||
|
||||
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);
|
||||
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);
|
||||
obcb_send_full_state_request(ws, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
const char *cursor_str = TextFormat(
|
||||
"Cursor:{xy:%5d:%5d}{i:%10d}{c:%4d}", cur_x, cur_y, index, chunk);
|
||||
DrawTextShadow(font, cursor_str, x, win_size.y - 8 - 12 * 2, 14, WHITE);
|
||||
|
||||
x += MeasureTextEx(font, cursor_str, 14, 0).x + 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
float y = win_size.y - (WAITING_QUEUE_SIZE * 12) - 10 - 12 * 4;
|
||||
DrawTextEx(font, "Queue:", (Vector2) { 8, y }, 14, 0, WHITE);
|
||||
y += 12;
|
||||
for (int i = 0; i < WAITING_QUEUE_SIZE; i++) {
|
||||
struct waiting_queue_elem *spot = &waiting_queue[i];
|
||||
DrawTextShadow(font, TextFormat(
|
||||
"%2d: %4d [%c|%c] %.2fs ago",
|
||||
i,
|
||||
spot->index,
|
||||
spot->waiting ? 'W' : ' ',
|
||||
spot->occupied ? 'O' : ' ',
|
||||
current_time - spot->waiting_since),
|
||||
8, y + i * 12, 14, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
if (subscribed_chunk_id >= 0) {
|
||||
DrawTexture(subscribed.tex, win_size.x - 512, win_size.y - 512, RED);
|
||||
}
|
||||
}
|
||||
EndDrawing();
|
||||
}
|
||||
|
@ -173,3 +388,12 @@ static void obcb_send_full_state_request(struct mg_connection *c, uint16_t 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++) {
|
||||
for (int ox = -2; ox <= 2; ox++) {
|
||||
DrawTextEx(font, txt, Vector2Add(pos, (Vector2) { ox, oy }), size, 0, BLACK);
|
||||
}
|
||||
}
|
||||
DrawTextEx(font, txt, pos, size, 0, color);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ struct OBCB_MSG(full_state_response, SERVER) {
|
|||
};
|
||||
|
||||
struct OBCB_MSG(partial_state_update, SERVER) {
|
||||
uint16_t offset; // in bytes, global
|
||||
uint32_t offset; // in bytes, global
|
||||
uint8_t chunk[OBCB_UPDATE_CHUNK_SIZE];
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue