use bufio; use fmt; use net; use net::tcp; use net::ip; use io; use os; use fs; use strings; use time; use time::date; use time::chrono; use unix::poll; use drawing; use drawing::{pos}; use packet_reader; use bytes; def PORT: u16 = 41460; type conn = net::socket; def CHUNKSIZE = 512; export fn main() void = { // create 4 pictures. later we will have more pictures // but for now there are just 4, and they are at fixed positions const offs: [_]pos = [ (0,0), (0,CHUNKSIZE), (CHUNKSIZE,0), (CHUNKSIZE,CHUNKSIZE), ]; let pictures: []drawing::picture = alloc([],len(offs)); for (let i = 0z; i < len(offs); i +=1){ append(pictures, load_picture_from_file(offs[i])!); }; const listener = match(tcp::listen(ip::ANY_V4, PORT, tcp::reuseaddr)) { case let err: net::error => fmt::fatalf(net::strerror(err)); case let sock: net::socket => yield sock; }; loop(listener, pictures); }; def save_interval = 20 * time::SECOND; // caller should free return value fn filename_from_world_pos(pos: pos) str = fmt::asprintf("./c.{}.{}.ppm",pos.0,pos.1); fn save_world(pictures: []drawing::picture) void = { fmt::printfln("saving world!")!; for (const pic &.. pictures) { const filename = filename_from_world_pos(pic.world_pos); fmt::printfln("\t-> {}",filename)!; defer free(filename); const mode = fs::mode::USER_RW | fs::mode::GROUP_RW; const file = os::create(filename, mode)!; defer { fmt::printfln("\t. {}",filename)!; io::close(file)!; }; const header = fmt::asprintf("P6\n{} {}\n{}\n", pic.w, pic.h, 255); defer free(header); io::writeall(file, strings::toutf8(header))!; let buf: []u8 = alloc([0...],3*pic.w*pic.h); defer free(buf); for (let i = 0z; i < pic.w * pic.h; i += 1) { const px = pic.d[i]; buf[3*i ] = ((px>>16)&0xff): u8; buf[3*i+1] = ((px>>8) &0xff): u8; buf[3*i+2] = ((px) &0xff): u8; }; io::writeall(file, buf)!; }; }; type bad_header = !void; fn load_picture_from_file(world_pos: pos) (drawing::picture | fs::error | bad_header) = { const filename = filename_from_world_pos(world_pos); defer free(filename); const file = os::open(filename)?; fmt::printfln("reading from {}",filename)!; defer { fmt::printfln("closing {}",filename)!; match (io::close(file)) { case let err: io::error => fmt::println("error",io::strerror(err))!; case => yield; }; }; const header = fmt::asprintf("P6\n{} {}\n{}\n",CHUNKSIZE,CHUNKSIZE,255); defer free(header); const header_bytes = strings::toutf8(header); let header_buf: []u8 = alloc([0...],len(header_bytes)); defer free(header_buf); io::readall(file, header_buf)?; if (!bytes::equal(header_buf, header_bytes)) return bad_header; let file_buf: []u8 = alloc([0...], 3*CHUNKSIZE*CHUNKSIZE); defer free(file_buf); io::readall(file, file_buf)?; let picture_buf: []u32 = alloc([0...], CHUNKSIZE*CHUNKSIZE); for (let i = 0z; i < len(picture_buf); i += 1) { picture_buf[i] = (file_buf[3*i]:u32<<16) |(file_buf[3*i+1]:u32<<8) |(file_buf[3*i+2]:u32); }; return drawing::picture { w = CHUNKSIZE, h = CHUNKSIZE, d = picture_buf: *[*]u32, world_pos = world_pos, }; }; fn loop(listener: net::socket, pictures: []drawing::picture) void = { // pollfds[0] is the listener // pollfds[n>0] corresponds to packet_readers[n-1] let pollfds: []poll::pollfd = alloc([ poll::pollfd { fd = listener, events = poll::event::POLLIN, revents = 0 }]); let packet_readers: []packet_reader::packet_reader = []; let now = time::now(time::clock::MONOTONIC); let next = time::add(now, save_interval); save_world(pictures); for (true) { fmt::println("poll.")!; const timeout = time::diff(now, next); poll::poll(pollfds, timeout)!; now = time::now(time::clock::MONOTONIC); if (time::compare(now, next) >= 0) { save_world(pictures); next = time::add(now, save_interval); }; if (0 != pollfds[0].revents & poll::event::POLLIN) { fmt::println("new conn")!; const new_conn = tcp::accept(pollfds[0].fd)!; fmt::println("a")!; append(pollfds, poll::pollfd { fd = new_conn, events = poll::event::POLLIN, revents = 0 }); append(packet_readers, packet_reader::new()); fmt::printfln("there are now {},{} conns.",len(pollfds),len(packet_readers))!; send_world(new_conn, pictures)!; }; for (let connidx = 1z; connidx < len(pollfds); connidx += 1) { if (0 != pollfds[connidx].revents & poll::event::POLLIN) { match (packet_reader::read(&packet_readers[connidx-1], pollfds[connidx].fd)) { case void => for (const packet => packet_reader::next(&packet_readers[connidx-1])!) { handle_packet(pictures, pollfds, packet, connidx); }; case let err: io::error => fmt::printfln("#{} error: {}",connidx,io::strerror(err))!; delete(pollfds[connidx]); delete(packet_readers[connidx-1]); connidx -= 1; case io::EOF => fmt::printfln("#{} disconnect",connidx)!; delete(pollfds[connidx]); delete(packet_readers[connidx-1]); connidx -= 1; }; }; }; }; }; // ehh, really need a struct to put pollfds, pictures, connections... in fn handle_packet(pictures: []drawing::picture, pollfds: []poll::pollfd, packet: packet_reader::packet, connidx: size) void = { match (packet) { case let op: packet_reader::packet_drawop => const opc = op as drawing::op_circle; fmt::printfln("#{}: drawop ({:+6},{:+6})",connidx,opc.0,opc.1)!; drawing::perform(pictures, opc); for (let otheridx = 1z; otheridx < len(pollfds); otheridx += 1) { if (otheridx == connidx) continue; fmt::printfln("\t -> #{}",otheridx)!; // ehhh shouldn't reserialize for each other thing packet_reader::send(pollfds[otheridx].fd, packet)!; }; case => abort("other packets not supported yet"); }; }; fn send_world(conn: io::file, pictures: []drawing::picture) (void | io::error) = { for (const pic &.. pictures) { fmt::printfln(" sending {},{}",pic.world_pos.0, pic.world_pos.1)!; packet_reader::send(conn, packet_reader::packet_sendchunk { world_pos = pic.world_pos, chunk_data = pic.d[..pic.w*pic.h], })?; }; };