use bufio; use bytes; use errors; use fmt; use fs; use io; use net::ip; use net::tcp; use net; use os; use strings; use time::chrono; use time::date; use time; use unix::poll; use drawing; use drawing::{pos}; use packet_reader; def PORT: u16 = 41460; def CHUNKSIZE = 512; 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 save_interval = 20 * time::SECOND; 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, 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)); case let sock: net::socket => yield sock; }; 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); }; }; }; 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 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]); }; }; }; 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; }; }; }; 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})",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 => fmt::printfln("shouldn't be getting other packets from #{}",conn_idx)!; state.connections[conn_idx].should_delete = true; }; }; 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], })?; }; };