diff options
-rw-r--r-- | packet_reader/packet_reader.ha | 5 | ||||
-rw-r--r-- | server/main.ha | 328 | ||||
-rw-r--r-- | todo | 4 |
3 files changed, 249 insertions, 88 deletions
diff --git a/packet_reader/packet_reader.ha b/packet_reader/packet_reader.ha index 02ecdba..30911df 100644 --- a/packet_reader/packet_reader.ha +++ b/packet_reader/packet_reader.ha @@ -69,9 +69,12 @@ export fn next(pr: *packet_reader) (packet | done | error) = { // and return the packet, // or, ascertain there is no full packet, and return done if (len(pr.good) < size(u32)) return done; + fmt::println("a")!; const packet_len = endian::legetu32(pr.good[0..4]); if (packet_len < 8) return "packet size field too small": error; + fmt::println("b")!; if (len(pr.good) < packet_len) return done; + fmt::println("c")!; const packet_bytes = pr.good[..packet_len]; pr.good = pr.good[packet_len..]; @@ -80,6 +83,7 @@ export fn next(pr: *packet_reader) (packet | done | error) = { switch (ty) { case packet_type::DRAW_OP => const op = drawing::deser_op(packet_bytes[8..]); + fmt::println("d")!; return op: packet_drawop; case packet_type::SEND_CHUNK => // return value is BORROWED from the BUFFER @@ -91,6 +95,7 @@ export fn next(pr: *packet_reader) (packet | done | error) = { ): drawing::pos; const d = cast_u8s_to_u32s(chunk_data_bytes); assert(len(d) == 512*512,"wrong chunk size??"); + fmt::println("e")!; return packet_sendchunk { world_pos = pos, chunk_data = d }; }; }; diff --git a/server/main.ha b/server/main.ha index 4761595..6beeee3 100644 --- a/server/main.ha +++ b/server/main.ha @@ -1,27 +1,41 @@ use bufio; +use bytes; +use errors; use fmt; -use net; -use net::tcp; -use net::ip; +use fs; use io; +use net::ip; +use net::tcp; +use net; use os; -use fs; use strings; -use time; -use time::date; use time::chrono; +use time::date; +use time; use unix::poll; + use drawing; use drawing::{pos}; use packet_reader; -use bytes; def PORT: u16 = 41460; +def CHUNKSIZE = 512; -type conn = net::socket; +type server_state = struct { + connections: []connection, + pictures: []drawing::picture, + listener: net::socket, + running: bool +}; + +type connection = struct { + pr: packet_reader::packet_reader, + sock: net::socket, + pos: pos, + should_delete: bool, +}; -def CHUNKSIZE = 512; export fn main() void = { // create 4 pictures. later we will have more pictures @@ -32,10 +46,13 @@ export fn main() void = { let pictures: []drawing::picture = alloc([],len(offs)); for (let i = 0z; i < len(offs); i +=1){ - append(pictures, load_picture_from_file(offs[i])!); + append(pictures, match (load_picture_from_file(offs[i])) { + case let pic: drawing::picture => yield pic; + case let err: fs::error => fmt::fatal(fs::strerror(err)); + case bad_header => fmt::fatal("bad ppm header"); + }); }; - const listener = match(tcp::listen(ip::ANY_V4, PORT, tcp::reuseaddr)) { case let err: net::error => fmt::fatalf(net::strerror(err)); @@ -43,7 +60,35 @@ export fn main() void = { yield sock; }; - loop(listener, pictures); + let state = server_state { + connections = [], + pictures = pictures, + listener = listener, + running = true, + }; + + let now = time::now(time::clock::MONOTONIC); + let next = time::add(now, save_interval); + + for (state.running) { + const timeout = time::diff(now, next); + fmt::println("running loop, timeout {}",timeout)!; + loop(&state, timeout); + + now = time::now(time::clock::MONOTONIC); + if (time::compare(now, next) >= 0) { + fmt::println("saving world!")!; + match (save_world(&state)) { + case let e: fs::error => + fmt::fatalf("couldn't save world: {}",fs::strerror(e)); + case void => yield; + }; + next = time::add(now, save_interval); + }; + }; + + + }; def save_interval = 20 * time::SECOND; @@ -52,22 +97,18 @@ def save_interval = 20 * time::SECOND; fn filename_from_world_pos(pos: pos) str = fmt::asprintf("./c.{}.{}.ppm",pos.0,pos.1); -fn save_world(pictures: []drawing::picture) void = { +fn save_world(state: *server_state) (void | fs::error) = { fmt::printfln("saving world!")!; - for (const pic &.. pictures) { + for (const pic &.. state.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 file = os::create(filename, mode)?; + defer io::close(file): void; - const header = fmt::asprintf("P6\n{} {}\n{}\n", pic.w, pic.h, 255); - defer free(header); - io::writeall(file, strings::toutf8(header))!; + fmt::fprintf(file, "P6\n{} {}\n{}\n", pic.w, pic.h, 255)?; let buf: []u8 = alloc([0...],3*pic.w*pic.h); defer free(buf); @@ -77,16 +118,33 @@ fn save_world(pictures: []drawing::picture) void = { buf[3*i+1] = ((px>>8) &0xff): u8; buf[3*i+2] = ((px) &0xff): u8; }; - io::writeall(file, buf)!; + io::writeall(file, buf)?; }; }; +fn new_picture(world_pos: pos) drawing::picture = { + let picture_buf: []u32 = alloc([0xffffff...], CHUNKSIZE*CHUNKSIZE); + return drawing::picture { + w = CHUNKSIZE, + h = CHUNKSIZE, + d = picture_buf: *[*]u32, + world_pos = world_pos, + }; +}; + type bad_header = !void; + +// loads the chunk at the given position from the filesystem, +// or, if no file is found, creates a fresh new chunk 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)?; + const file = match (os::open(filename)) { + case let f: io::file => yield f; + case errors::noentry => return new_picture(world_pos); + case let e: fs::error => return e; + }; fmt::printfln("reading from {}",filename)!; defer { fmt::printfln("closing {}",filename)!; @@ -121,86 +179,180 @@ fn load_picture_from_file(world_pos: pos) (drawing::picture | fs::error | bad_he }; }; +// fn old_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; +// }; +// }; +// }; +// }; +// }; + +fn loop(state: *server_state, timeout: time::duration) void = { + // do poll. + let pollfds: []poll::pollfd = []; + append(pollfds, poll::pollfd { + fd = state.listener, + events = poll::event::POLLIN, + ... + }); + for (const conn &.. state.connections) { + append(pollfds, poll::pollfd { + fd = conn.sock, + events = poll::event::POLLIN, + ... + }); + }; + poll::poll(pollfds, timeout)!; + + // if listener: add connection + if (0 != pollfds[0].revents & poll::event::POLLIN) { + const new_conn = tcp::accept(pollfds[0].fd)!; + append(state.connections, connection { + sock = new_conn, + pr = packet_reader::new(), + pos = (0,0), + should_delete = false, + }); + fmt::printfln("there are now {} connections", + len(state.connections))!; + greet_connection(state, len(state.connections)); + }; + // for each connection, handle packet(s) + // loop thru pollfds not state.connections so we don't + // try to look at the nonexistent pollfd for a connection we just accepted + for (let pollfd_idx = 1z: size; pollfd_idx < len(pollfds); pollfd_idx += 1) { + const conn_idx = pollfd_idx - 1; + fmt::println("checking conn {} with pollfd {}",conn_idx, pollfd_idx)!; + if (0 != pollfds[conn_idx+1].revents & poll::event::POLLIN) { + read_from_connection(state, conn_idx); + }; + }; + perform_deletions(state); +}; - -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); +fn perform_deletions(state: *server_state) void = { + for (let i1 = len(state.connections); i1 > 0; i1 -= 1) { + const i = i1 - 1; + const conn = state.connections[i]; + if (conn.should_delete) { + fmt::printfln("deleting conn {}",i)!; + delete(state.connections[i]); }; + }; +}; - 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; - }; +fn greet_connection(state: *server_state, conn_idx: size) void = void; + + +fn read_from_connection(state: *server_state, conn_idx: size) void = { + const conn = &state.connections[conn_idx]; + match (packet_reader::read(&conn.pr, conn.sock)) { + case let err: io::error => + fmt::printfln("#{} error: {}", conn_idx, io::strerror(err))!; + conn.should_delete = true; + case io::EOF => + fmt::printfln("#{} disconnect", conn_idx)!; + conn.should_delete = true; + case void => + for (true) match (packet_reader::next(&conn.pr)) { + // foreach loop seems to break match exhaustivity here + case done => break; + case let p: packet_reader::packet => + handle_packet(state, conn_idx, p); + case let e: packet_reader::error => + fmt::printfln("packet error {}",e)!; + conn.should_delete = true; + case => fmt::printfln("eoeugh")!; }; - }; }; }; -// 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 = { + + +fn handle_packet( + state: *server_state, + conn_idx: size, + packet: packet_reader::packet +) 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)!; + fmt::printfln("#{}: drawop ({:+6},{:+6})",conn_idx,opc.0,opc.1)!; + drawing::perform(state.pictures, opc); + for (let other_idx = 0z; + other_idx < len(state.connections); + other_idx += 1) + { + if (other_idx == conn_idx) continue; + const other_conn = &state.connections[other_idx]; + if (other_conn.should_delete) continue; + fmt::printfln("\t -> #{}",other_idx)!; + match (packet_reader::send(other_conn.sock, packet)) { + case void => yield; + case let e: io::error => + fmt::printfln("couldn't send to #{}: {}", + other_idx, io::strerror(e))!; + other_conn.should_delete = true; + }; }; case => - abort("other packets not supported yet"); + fmt::printfln("shouldn't be getting other packets from #{}",conn_idx)!; + state.connections[conn_idx].should_delete = true; }; }; @@ -0,0 +1,4 @@ +multiple chunks on server +client send position to server +server list of clients, properly +server periodically unloads chunks not near any player |