aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorubq323 <ubq323@ubq323.website>2024-04-16 12:34:18 +0100
committerubq323 <ubq323@ubq323.website>2024-04-16 12:34:18 +0100
commit187d6d32e71aface08c1c486874901ecc9b160c1 (patch)
treef6c7e69d099e5a6ffcc9ca88a41f9cf15b76d8e1
parent9bb892b35308919ff93d22e12387e6bdc0a64223 (diff)
fix chunkloading and unloading on client
-rw-r--r--client/main.ha151
-rw-r--r--client/pictures.ha145
-rw-r--r--drawing/drawing.ha23
-rw-r--r--server/main.ha2
-rw-r--r--todo1
5 files changed, 172 insertions, 150 deletions
diff --git a/client/main.ha b/client/main.ha
index 9b8f92b..0f4b1b8 100644
--- a/client/main.ha
+++ b/client/main.ha
@@ -7,12 +7,12 @@ use net::dial;
use unix::poll;
use drawing;
-use drawing::{pos,CHUNKSIZE};
+use drawing::{pos,CHUNKSIZE,picture};
use client::paintui;
use packet_reader;
use packet_reader::{VERSION};
-def NCHUNKS = 16;
+def NSURFS = 16;
def WIN_H: i32 = 480;
def WIN_W: i32 = 640;
@@ -27,22 +27,7 @@ export fn main() void = {
const wsurf = sdl2::SDL_GetWindowSurface(win)!;
- let offs: []pos = [];
- for (let x = -1; x < 3; x+=1) for (let y = -1; y < 3; y+=1)
- append(offs, (1024*1024+x*CHUNKSIZE,y*CHUNKSIZE));
-
- let pictures: []drawing::picture = alloc([],NCHUNKS);
- let picture_surfaces: []*sdl2::SDL_Surface = alloc([], NCHUNKS);
- for (let i = 0z; i < NCHUNKS; i +=1){
- const surf = sdl2::SDL_CreateRGBSurface(0,
- CHUNKSIZE, CHUNKSIZE, 32, 0xff0000, 0xff00, 0xff, 0)!;
- append(picture_surfaces, surf);
- append(pictures, picture_from_surface(surf, offs[i]));
- };
- for (const p &.. pictures) {
- drawing::clear_picture(p, 0x00ff00);
- drawing::outline_picture(p);
- };
+ let pmgr = picture_mgr { ... };
// connect to server
const conn = match(dial::dial("tcp","localhost","41460")) {
@@ -51,6 +36,7 @@ export fn main() void = {
fmt::fatal("couldn't connect to server:",net::strerror(err));
};
+ // version check
let byte = [0u8];
match (io::read(conn, byte)) {
case let e: io::error =>
@@ -60,7 +46,6 @@ export fn main() void = {
byte[0], VERSION);
};
-
const pollfd: [1]poll::pollfd = [ poll::pollfd {
fd = conn, events=poll::event::POLLIN, revents = 0
}];
@@ -74,16 +59,14 @@ export fn main() void = {
let n = 0;
let lasttime = sdl2::SDL_GetTicks();
- let requested_chunks: []pos = [];
-
// in SCREEN coords
let mouse_pos: pos = (0,0);
let mouse_down = false;
- request_visible_chunks(pictures, conn, camera_pos, &requested_chunks);
- const win_pic = picture_from_surface(wsurf, (9,9));
+ process_chunk_loadedness(&pmgr, conn, camera_pos);
+ const win_pic = picture_from_surface(wsurf, (9,9));
for (!quit) {
const did_move = do_movement(&camera_pos);
@@ -124,13 +107,13 @@ export fn main() void = {
match (paintui::tick(&pstate, mouse_pos_world, mouse_down)) {
case void => yield;
case let op: drawing::op =>
- drawing::perform(pictures, op);
+ perform_drawop(&pmgr, op);
packet_reader::send(conn, op: packet_reader::packet_drawop)!;
};
if (did_move) {
packet_reader::send(conn, camera_pos: packet_reader::packet_position)!;
- request_visible_chunks(pictures, conn, camera_pos, &requested_chunks);
+ process_chunk_loadedness(&pmgr, conn, camera_pos);
};
const n = poll::poll(pollfd, poll::NONBLOCK)!;
@@ -139,36 +122,17 @@ export fn main() void = {
for (const packet => packet_reader::next(&packet_reader)!) {
match (packet) {
case let op: packet_reader::packet_drawop =>
- drawing::perform(pictures, op);
+ perform_drawop(&pmgr, op);
case let packet: packet_reader::packet_sendchunk =>
- assert(packet.world_pos.0 % CHUNKSIZE == 0
- && packet.world_pos.1 % CHUNKSIZE == 0, "bad chunk world pos");
- const pic = find_picture_for_chunkdata(camera_pos, packet.world_pos, pictures);
- pic.world_pos = packet.world_pos;
- pic.d[..len(packet.chunk_data)] = packet.chunk_data[..];
-
- for (let i = 0z; i < len(requested_chunks); i += 1) {
- const rc = requested_chunks[i];
- if (rc.0 == packet.world_pos.0 && rc.1 == packet.world_pos.1) {
- delete(requested_chunks[i]);
- fmt::printfln("fulfilling chunk request {},{}",
- packet.world_pos.0, packet.world_pos.1)!;
- break;
- };
- };
-
+ enact_chunkdata(&pmgr, packet, camera_pos);
};
};
};
-
drawing::clear_picture(&win_pic, 0xdddddd);
- for (let i = 0z; i < len(pictures); i += 1) {
- const psurf = picture_surfaces[i];
- const pic = &pictures[i];
- render_picture(pic, psurf, wsurf, camera_pos);
- };
+ for (const pic &.. pmgr.pictures)
+ render_picture(pic, wsurf, camera_pos);
drawing::circle_hollow(&win_pic, mouse_pos, paintui::sizes[pstate.size_idx]: i32, pstate.color);
@@ -185,8 +149,8 @@ fn picture_from_surface(surf: *sdl2::SDL_Surface, world_pos: pos) drawing::pictu
world_pos = world_pos,
};
-fn render_picture(pic: *drawing::picture, surf: *sdl2::SDL_Surface, winsurf: *sdl2::SDL_Surface, camera_pos: pos) void = {
- sdl2::SDL_BlitSurface(surf, null, winsurf, &sdl2::SDL_Rect{
+fn render_picture(pic: *picture_c, winsurf: *sdl2::SDL_Surface, camera_pos: pos) void = {
+ sdl2::SDL_BlitSurface(pic.surface, null, winsurf, &sdl2::SDL_Rect{
x = pic.world_pos.0 - camera_pos.0, y = pic.world_pos.1 - camera_pos.1, ...
})!;
};
@@ -216,91 +180,4 @@ fn do_movement(pos: *pos) bool = {
return did_move;
};
-fn is_picture_visible(camera_pos: pos, pic_pos: pos) bool = {
- const s_min: pos = camera_pos;
- const s_max: pos = (camera_pos.0 + WIN_W, camera_pos.1 + WIN_H);
- const p_min: pos = pic_pos;
- const p_max: pos = (pic_pos.0 + CHUNKSIZE: i32, pic_pos.1 + CHUNKSIZE: i32);
- return (s_min.0 <= p_max.0 && s_max.0 >= p_min.0)
- && (s_min.1 <= p_max.1 && s_max.1 >= p_min.1);
-};
-
-fn find_picture_for_chunkdata(camera_pos: pos, world_pos: pos, pictures: []drawing::picture) *drawing::picture = {
- // if we have one with that exact worldpos, return that
- // otherwise find one that isn't visible
-
- let invisible: nullable *drawing::picture = null;
-
- for (const pic &.. pictures) {
- if (pic.world_pos.0 == world_pos.0 && pic.world_pos.1 == world_pos.1) {
- // fmt::printfln("found already pic at {},{}",pic.world_pos.0, pic.world_pos.1)!;
- return pic;
- };
- if (invisible == null && !is_picture_visible(camera_pos, pic.world_pos)) {
- // fmt::printfln("can recycle pic at {},{}",pic.world_pos.0,pic.world_pos.1)!;
- invisible = pic;
- };
- };
-
- match (invisible) {
- case null => abort("couldn't find offscreen picture???");
- case let p: *drawing::picture => return p;
- };
-
-};
-
-
-
-fn floor_div(a: i32, b: i32) i32 = {
- if (a < 0) return -1-floor_div(-(a+1),b);
- return a / b;
-};
-fn find_chunk(pictures: []drawing::picture, world_pos: pos)
- nullable *drawing::picture = {
- for (const pic &.. pictures) {
- if (pic.world_pos.0 == world_pos.0
- && pic.world_pos.1 == world_pos.1) {
- return pic;
- };
- };
- return null;
-};
-
-fn request_visible_chunks(
- pictures: []drawing::picture,
- conn: net::socket,
- camera_pos: pos,
- requested_chunks: *[]pos,
-) void = {
- const x0 = CHUNKSIZE*floor_div(camera_pos.0,CHUNKSIZE);
- const y0 = CHUNKSIZE*floor_div(camera_pos.1,CHUNKSIZE);
-
- for (let dx = 0i32; dx < 3; dx += 1) for (let dy = 0i32; dy < 3; dy += 1) {
- let world_pos = (x0+CHUNKSIZE*dx, y0+CHUNKSIZE*dy): pos;
-
- for (const rc .. requested_chunks) {
- if (rc.0 == world_pos.0 && rc.1 == world_pos.1) {
- fmt::printfln("not requesting {},{} again", rc.0, rc.1)!;
- return;
- };
- };
-
-
- if (!is_picture_visible(camera_pos, world_pos)) {
- // fmt::printfln("invisible {},{}", world_pos.0, world_pos.1)!;
- continue;
- };
- // fmt::printfln("visible {},{}, checking availability", world_pos.0, world_pos.1)!;
- match (find_chunk(pictures,world_pos)) {
- case *drawing::picture =>
- // fmt::printfln("\talready got that one")!;
- yield;
- case null =>
- fmt::printfln("\trequesting {},{}", world_pos.0, world_pos.1)!;
- packet_reader::send(conn, world_pos: packet_reader::packet_reqchunk)!;
- append(requested_chunks, world_pos);
- };
- };
-};
-
diff --git a/client/pictures.ha b/client/pictures.ha
new file mode 100644
index 0000000..1db3954
--- /dev/null
+++ b/client/pictures.ha
@@ -0,0 +1,145 @@
+use fmt;
+use net;
+
+use sdl2;
+
+use drawing::{picture,pos,CHUNKSIZE};
+use drawing;
+use packet_reader;
+
+type picture_mgr = struct {
+ pictures: []picture_c,
+ requests: []pos,
+};
+
+type picture_c = struct {
+ picture,
+ surface: *sdl2::SDL_Surface,
+};
+
+
+fn is_picture_visible(camera_pos: pos, pic_pos: pos) bool = {
+ const s_min: pos = camera_pos;
+ const s_max: pos = (camera_pos.0 + WIN_W, camera_pos.1 + WIN_H);
+ const p_min: pos = pic_pos;
+ const p_max: pos = (pic_pos.0 + CHUNKSIZE: i32, pic_pos.1 + CHUNKSIZE: i32);
+ return (s_min.0 <= p_max.0 && s_max.0 >= p_min.0)
+ && (s_min.1 <= p_max.1 && s_max.1 >= p_min.1);
+};
+
+fn find_picture_by_pos(pmgr: *picture_mgr, world_pos: pos)
+ nullable *picture = {
+ for (const pic &.. pmgr.pictures) {
+ if (pic.world_pos.0 == world_pos.0
+ && pic.world_pos.1 == world_pos.1) {
+ return pic;
+ };
+ };
+ return null;
+};
+
+// called when a new chunkdata packet comes in
+fn enact_chunkdata(pmgr: *picture_mgr,
+ packet: packet_reader::packet_sendchunk,
+ camera_pos: pos,
+) void = {
+ assert(packet.world_pos.0 % CHUNKSIZE == 0
+ && packet.world_pos.1 % CHUNKSIZE == 0,
+ "bad chunk world pos");
+ assert(len(packet.chunk_data) == CHUNKSIZE*CHUNKSIZE,
+ "bad chunk data size");
+
+ const surf = sdl2::SDL_CreateRGBSurface(0,
+ CHUNKSIZE, CHUNKSIZE, 32, 0xff0000, 0xff00, 0xff, 0)!;
+ append(pmgr.pictures, picture_c {
+ w = CHUNKSIZE,
+ h = CHUNKSIZE,
+ d = (surf.pixels as *opaque: *[*]u32),
+ world_pos = packet.world_pos,
+ surface = surf,
+ });
+
+ const p = &pmgr.pictures[len(pmgr.pictures)-1];
+ p.d[..p.w*p.h] = packet.chunk_data[..];
+
+ // delete extant request
+ for (let i = 0z; i < len(pmgr.requests); i += 1) {
+ const rc = pmgr.requests[i];
+ if (rc.0 == packet.world_pos.0 && rc.1 == packet.world_pos.1) {
+ delete(pmgr.requests[i]);
+ break;
+ };
+ };
+};
+
+fn floor_div(a: i32, b: i32) i32 = {
+ if (a < 0) return -1-floor_div(-(a+1),b);
+ return a / b;
+};
+
+
+fn is_request_inflight(pmgr: *picture_mgr, world_pos: pos) bool = {
+ for (const rc .. pmgr.requests)
+ if (rc.0 == world_pos.0 && rc.1 == world_pos.1)
+ return true;
+ return false;
+};
+
+// called on startup and on camera movement
+// for each loaded chunk not visible, unload it
+// for each chunk that should be visible that isn't loaded
+// and which hasn't already been requested,
+// request it.
+fn process_chunk_loadedness(
+ pmgr: *picture_mgr,
+ conn: net::socket,
+ camera_pos: pos,
+) void = {
+
+ // unload all invisible chunks
+ for (let i1 = len(pmgr.pictures); i1 > 0; i1 -= 1) {
+ const i = i1 - 1;
+ const pic = pmgr.pictures[i];
+ if (!is_picture_visible(camera_pos, pic.world_pos)) {
+ fmt::printfln("## unloading {},{}", pic.world_pos.0, pic.world_pos.1)!;
+ delete(pmgr.pictures[i]);
+ };
+ };
+
+
+ const x0 = CHUNKSIZE*floor_div(camera_pos.0,CHUNKSIZE);
+ const y0 = CHUNKSIZE*floor_div(camera_pos.1,CHUNKSIZE);
+
+ // use a 3x3 window for 'chunks that might need to be loaded'
+ for (let dx = 0i32; dx < 3; dx += 1) {
+ for (let dy = 0i32; dy < 3; dy += 1) {
+ const world_pos = (x0 + CHUNKSIZE*dx, y0 + CHUNKSIZE*dy): pos;
+ const visible = is_picture_visible(camera_pos, world_pos);
+ const loaded = !(find_picture_by_pos(pmgr, world_pos) is null);
+ const requested = is_request_inflight(pmgr, world_pos);
+
+ if (!visible) {
+ fmt::printfln("\tnot visible {},{}", world_pos.0, world_pos.1)!;
+ continue;
+ };
+ if (loaded) {
+ fmt::printfln("\talready loaded {},{}", world_pos.0, world_pos.1)!;
+ continue;
+ };
+ if (requested) {
+ fmt::printfln("\tnot requesting again {},{}", world_pos.0, world_pos.1)!;
+ continue;
+ };
+
+ fmt::printfln("!! requesting {},{}", world_pos.0, world_pos.1)!;
+ packet_reader::send(conn, world_pos: packet_reader::packet_reqchunk)!;
+ append(pmgr.requests, world_pos);
+ };
+ };
+};
+
+
+fn perform_drawop(pmgr: *picture_mgr, op: drawing::op) void = {
+ for (const pic &.. pmgr.pictures)
+ drawing::perform(pic, op);
+};
diff --git a/drawing/drawing.ha b/drawing/drawing.ha
index 54116a3..ae36501 100644
--- a/drawing/drawing.ha
+++ b/drawing/drawing.ha
@@ -127,23 +127,24 @@ export fn stroke(picture: *picture, c0: pos, c1: pos, r: i32, color: u32) void =
};
// ehh should check bounding box instead of doing all pictures maybe
-export fn perform(pictures: []picture, op: op) void = {
+export fn perform(pic: *picture, op: op) void = {
match (op) {
case let o: op_circle =>
const x = o.pos.0, y=o.pos.1;
const r = o.radius;
const c = o.color & 0xffffff;
- for (const pic &.. pictures) {
- let pos_within_pic =
- (x - pic.world_pos.0, y - pic.world_pos.1): pos;
- circle(pic, pos_within_pic, r: i32, c);
- };
+ const pos_within_pic: pos =
+ (x - pic.world_pos.0, y - pic.world_pos.1);
+ circle(pic, pos_within_pic, r: i32, c);
case let s: op_stroke =>
const col = s.color & 0xffffff;
- for (const pic &.. pictures) {
- const c0 = (s.pos0.0 - pic.world_pos.0, s.pos0.1 - pic.world_pos.1);
- const c1 = (s.pos1.0 - pic.world_pos.0, s.pos1.1 - pic.world_pos.1);
- stroke(pic, c0, c1, s.radius: i32, col);
- };
+ const c0 = (s.pos0.0 - pic.world_pos.0, s.pos0.1 - pic.world_pos.1);
+ const c1 = (s.pos1.0 - pic.world_pos.0, s.pos1.1 - pic.world_pos.1);
+ stroke(pic, c0, c1, s.radius: i32, col);
};
};
+
+export fn perform_multi(pictures: []picture, op: op) void = {
+ for (const pic &.. pictures)
+ perform(pic, op);
+};
diff --git a/server/main.ha b/server/main.ha
index 2043a1d..41e6b6a 100644
--- a/server/main.ha
+++ b/server/main.ha
@@ -199,7 +199,7 @@ fn handle_packet(
case let op: packet_reader::packet_drawop =>
// fmt::printfln("#{}: drawop ({:+6},{:+6}) r{}",
// conn_idx,opc.pos.0,opc.pos.1, opc.radius)!;
- drawing::perform(state.pictures, op);
+ drawing::perform_multi(state.pictures, op);
for (let other_idx = 0z;
other_idx < len(state.connections);
other_idx += 1)
diff --git a/todo b/todo
index 105a3ca..2c2c176 100644
--- a/todo
+++ b/todo
@@ -1,3 +1,2 @@
-smooth brush strokes
rle compression
more elegant solution to big packets than just having a giant buffer