use fmt; use sdl2; use math; use io; use net; use net::dial; use unix::poll; use drawing; use drawing::{pos,CHUNKSIZE}; use client::paintui; use packet_reader; use packet_reader::{VERSION}; def NCHUNKS = 16; def WIN_H: i32 = 480; def WIN_W: i32 = 640; export fn main() void = { // sdl init stuff sdl2::SDL_Init(sdl2::SDL_INIT_VIDEO)!; defer sdl2::SDL_Quit(); const win = sdl2::SDL_CreateWindow("garden", sdl2::SDL_WINDOWPOS_UNDEFINED, sdl2::SDL_WINDOWPOS_UNDEFINED, 640, 480, sdl2::SDL_WindowFlags::NONE)!; defer sdl2::SDL_DestroyWindow(win); 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); }; // connect to server const conn = match(dial::dial("tcp","localhost","41460")) { case let c: net::socket => yield c; case let err: net::error => fmt::fatal("couldn't connect to server:",net::strerror(err)); }; let byte = [0u8]; match (io::read(conn, byte)) { case let e: io::error => fmt::fatal("error with server handshake",io::strerror(e)); case => if (byte[0] != VERSION) fmt::fatalf("invalid version: got {}, expecting {}", byte[0], VERSION); }; const pollfd: [1]poll::pollfd = [ poll::pollfd { fd = conn, events=poll::event::POLLIN, revents = 0 }]; const packet_reader = packet_reader::new(); // paintui state let pstate = paintui::state { size_idx = 4, ... }; let camera_pos: pos = (25, 50); let quit = false; 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)); for (!quit) { const did_move = do_movement(&camera_pos); // let nvis = 0; // for (const pic &.. pictures) { // if (is_picture_visible(camera_pos, pic)) { // fmt::printfln(" {} {}",pic.world_pos.0, pic.world_pos.1)!; // nvis += 1; // }; // }; // fmt::printfln("{} visible",nvis)!; let ev = sdl2::event { ... }; for (sdl2::SDL_PollEvent(&ev)! == 1) switch (ev.event_type) { case sdl2::SDL_EventType::QUIT => quit = true; case sdl2::SDL_EventType::KEYDOWN => const keysym = ev.key.keysym.sym; if (keysym == sdl2::SDL_Keycode::ESCAPE) quit = true else if (sdl2::SDL_Keycode::ZERO <= keysym && keysym <= sdl2::SDL_Keycode::NINE) paintui::key(&pstate, keysym - sdl2::SDL_Keycode::ZERO); case sdl2::SDL_EventType::MOUSEBUTTONDOWN, sdl2::SDL_EventType::MOUSEBUTTONUP => const edata = ev.button; mouse_pos = (edata.x, edata.y); if (edata.button == 1) mouse_down = (edata.state == 1); case sdl2::SDL_EventType::MOUSEMOTION => const edata = ev.motion; mouse_pos = (edata.x, edata.y); case sdl2::SDL_EventType::MOUSEWHEEL => const edata = ev.wheel; paintui::mousewheel(&pstate, edata.y); case => void; }; const mouse_pos_world = (mouse_pos.0 + camera_pos.0, mouse_pos.1 + camera_pos.1); match (paintui::tick(&pstate, mouse_pos_world, mouse_down)) { case void => yield; case let op: drawing::op => drawing::perform(pictures, 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); }; const n = poll::poll(pollfd, poll::NONBLOCK)!; if (n > 0) { packet_reader::read(&packet_reader, conn)!; for (const packet => packet_reader::next(&packet_reader)!) { match (packet) { case let op: packet_reader::packet_drawop => drawing::perform(pictures, 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; }; }; }; }; }; 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); }; drawing::circle_hollow(&win_pic, mouse_pos, paintui::sizes[pstate.size_idx]: i32, pstate.color); sdl2::SDL_UpdateWindowSurface(win)!; n += 1; sdl2::SDL_Delay(1000/60); }; }; fn picture_from_surface(surf: *sdl2::SDL_Surface, world_pos: pos) drawing::picture = drawing::picture { w = surf.w: size, h = surf.h: size, d = (surf.pixels as *opaque: *[*]u32), 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{ x = pic.world_pos.0 - camera_pos.0, y = pic.world_pos.1 - camera_pos.1, ... })!; }; def SPEED = 17; def DIAG_SPEED = 12; // thereabouts // returned whether movement happened fn do_movement(pos: *pos) bool = { const kb = sdl2::SDL_GetKeyboardState(); let dx = 0; let dy = 0; if (kb[sdl2::SDL_Scancode::W]) dy -= 1; if (kb[sdl2::SDL_Scancode::S]) dy += 1; if (kb[sdl2::SDL_Scancode::A]) dx -= 1; if (kb[sdl2::SDL_Scancode::D]) dx += 1; const did_move = dx!=0 || dy!=0; let speed = SPEED; if (dx != 0 && dy != 0) speed = DIAG_SPEED; pos.0 += dx * speed; pos.1 += dy * speed; 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); }; }; };