aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packet_reader/packet_reader.ha5
-rw-r--r--server/main.ha328
-rw-r--r--todo4
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;
};
};
diff --git a/todo b/todo
new file mode 100644
index 0000000..70c70e8
--- /dev/null
+++ b/todo
@@ -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