aboutsummaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorubq323 <ubq323@ubq323.website>2024-04-14 00:35:19 +0100
committerubq323 <ubq323@ubq323.website>2024-04-14 00:35:19 +0100
commit6cff8d620cd92f9ddc63c6891b3e669b5294619e (patch)
treed3632c81992fdbf0f8435fa225920122bee29776 /server
parentc95e947d90a02091aef4e63693ce3205fdfba3b6 (diff)
multiple chunks on server and unload not near players. just need client to req chunks now
Diffstat (limited to 'server')
-rw-r--r--server/main.ha142
-rw-r--r--server/save_load.ha4
2 files changed, 108 insertions, 38 deletions
diff --git a/server/main.ha b/server/main.ha
index 1dffbb5..19ecc51 100644
--- a/server/main.ha
+++ b/server/main.ha
@@ -15,15 +15,15 @@ use time;
use unix::poll;
use drawing;
-use drawing::{pos};
+use drawing::{pos,CHUNKSIZE};
use packet_reader;
def PORT: u16 = 41460;
-def CHUNKSIZE = 512;
type server_state = struct {
connections: []connection,
+ // nb we use 'picture' and 'chunk' interchangeably
pictures: []drawing::picture,
listener: net::socket,
running: bool
@@ -39,20 +39,13 @@ type connection = struct {
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");
- });
- };
+ // 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)
@@ -65,7 +58,7 @@ export fn main() void = {
let state = server_state {
connections = [],
- pictures = pictures,
+ pictures = [],
listener = listener,
running = true,
};
@@ -75,7 +68,7 @@ export fn main() void = {
for (state.running) {
const timeout = time::diff(now, next);
- fmt::println("running loop, timeout {}",timeout)!;
+ fmt::printfln("running loop, timeout {} sec",timeout/time::SECOND)!;
loop(&state, timeout);
now = time::now(time::clock::MONOTONIC);
@@ -87,11 +80,9 @@ export fn main() void = {
case void => yield;
};
next = time::add(now, save_interval);
+ unload_distant_chunks(&state);
};
};
-
-
-
};
fn loop(state: *server_state, timeout: time::duration) void = {
@@ -123,7 +114,14 @@ fn loop(state: *server_state, timeout: time::duration) void = {
});
fmt::printfln("there are now {} connections",
len(state.connections))!;
- greet_connection(state, len(state.connections));
+ const conn_idx = len(state.connections) - 1;
+ match (greet_connection(state, conn_idx)) {
+ case void => yield;
+ case let e: io::error =>
+ fmt::printfln("error greeting {}: {}",conn_idx,
+ io::strerror(e))!;
+ delete(state.connections[conn_idx]);
+ };
};
// for each connection, handle packet(s)
@@ -131,7 +129,8 @@ fn loop(state: *server_state, timeout: time::duration) void = {
// 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 (state.connections[conn_idx].should_delete) continue;
+ fmt::printfln("checking conn {} with pollfd {}",conn_idx, pollfd_idx)!;
if (0 != pollfds[conn_idx+1].revents & poll::event::POLLIN) {
read_from_connection(state, conn_idx);
};
@@ -141,6 +140,7 @@ fn loop(state: *server_state, timeout: time::duration) void = {
};
+// kicks connections in an error state
fn perform_deletions(state: *server_state) void = {
for (let i1 = len(state.connections); i1 > 0; i1 -= 1) {
const i = i1 - 1;
@@ -152,7 +152,9 @@ fn perform_deletions(state: *server_state) void = {
};
};
-fn greet_connection(state: *server_state, conn_idx: size) void = void;
+fn greet_connection(state: *server_state, conn_idx: size)
+(void|io::error) = void;
+
fn read_from_connection(state: *server_state, conn_idx: size) void = {
@@ -166,10 +168,17 @@ fn read_from_connection(state: *server_state, conn_idx: size) void = {
conn.should_delete = true;
case void =>
for (true) match (packet_reader::next(&conn.pr)) {
- // foreach loop seems to break match exhaustivity here
+ // xxx foreach loop seems to break match exhaustivity here
+ // investigate that at some point
case done => break;
case let p: packet_reader::packet =>
- handle_packet(state, conn_idx, p);
+ match (handle_packet(state, conn_idx, p)) {
+ case void => yield;
+ case let e: io::error =>
+ fmt::printfln("handle packet error. #{}. {}",
+ conn_idx, io::strerror(e))!;
+ conn.should_delete = true;
+ };
case let e: packet_reader::error =>
fmt::printfln("packet error {}",e)!;
conn.should_delete = true;
@@ -181,11 +190,13 @@ fn handle_packet(
state: *server_state,
conn_idx: size,
packet: packet_reader::packet
-) void = {
+) (void|io::error) = {
+ const conn = &state.connections[conn_idx];
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)!;
+ 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);
@@ -203,19 +214,78 @@ fn handle_packet(
other_conn.should_delete = true;
};
};
+ case let pos: packet_reader::packet_position =>
+ fmt::printfln("pos of #{} is now {},{}",
+ conn_idx, pos.0, pos.1)!;
+ state.connections[conn_idx].pos = pos;
+ case let world_pos: packet_reader::packet_reqchunk =>
+ // xxx it is probably easy to ddos the server by sending lots of these
+ fmt::printfln("#{} requests chunk {},{}",
+ conn_idx, world_pos.0, world_pos.1)!;
+
+ const pic = ensure_chunk(state, world_pos);
+ packet_reader::send(conn.sock, packet_reader::packet_sendchunk {
+ world_pos = world_pos,
+ chunk_data = pic.d[..pic.w*pic.h],
+ })?;
+
case =>
- fmt::printfln("shouldn't be getting other packets from #{}",conn_idx)!;
+ 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],
- })?;
+fn ensure_chunk(state: *server_state, world_pos: pos) *drawing::picture = {
+ // if that chunk is already loaded, do nothing
+ // if not, load it from disk, or create it newly if not on disk
+ // in either case, return it
+ for (const pic &.. state.pictures) {
+ if (pic.world_pos.0 == world_pos.0 && pic.world_pos.1 == world_pos.1) {
+ return pic;
+ };
};
+ const pic = load_picture_from_file(world_pos)!;
+ append(state.pictures, pic);
+ return &state.pictures[len(state.pictures)-1];
+};
+
+fn floor_div(a: i32, b: i32) i32 = {
+ if (a < 0) return -1-floor_div(-(a+1),b);
+ return a / b;
};
+
+fn unload_distant_chunks(state: *server_state) void = {
+ const n_pics = len(state.pictures);
+ let should_unload = alloc([true...], n_pics);
+ defer free(should_unload);
+
+ for (const conn &.. state.connections) {
+ const chunk_in_x = floor_div(conn.pos.0, CHUNKSIZE)*CHUNKSIZE;
+ const chunk_in_y = floor_div(conn.pos.1, CHUNKSIZE)*CHUNKSIZE;
+ // pic_chunk_pos - player_chunk_pos must be either 0 or 512 or 1024
+ // can't be less than 0 or more than 1024
+
+ for (let i = 0z; i < n_pics; i += 1) {
+ const pic = &state.pictures[i];
+ const dcx = pic.world_pos.0 - chunk_in_x;
+ const dcy = pic.world_pos.1 - chunk_in_y;
+ if (0 <= dcx && dcx <= 2*CHUNKSIZE && 0 <= dcy && dcy <= 2*CHUNKSIZE)
+ should_unload[i] = false;
+ };
+ };
+
+ for (let i1 = n_pics; i1 > 0; i1 -= 1) {
+ const i = i1 - 1;
+ if (should_unload[i]) {
+ fmt::printfln("unloading {},{}",
+ state.pictures[i].world_pos.0,
+ state.pictures[i].world_pos.1)!;
+ delete(state.pictures[i]);
+ };
+ };
+};
+
+
+
diff --git a/server/save_load.ha b/server/save_load.ha
index cf82a0d..db5a18a 100644
--- a/server/save_load.ha
+++ b/server/save_load.ha
@@ -7,14 +7,13 @@ use os;
use strings;
use drawing;
-use drawing::{pos};
+use drawing::{pos,CHUNKSIZE};
// caller should free return value
fn filename_from_world_pos(pos: pos) str =
fmt::asprintf("./c.{}.{}.ppm",pos.0,pos.1);
fn save_world(state: *server_state) (void | fs::error) = {
- fmt::printfln("saving world!")!;
for (const pic &.. state.pictures) {
const filename = filename_from_world_pos(pic.world_pos);
fmt::printfln("\t-> {}",filename)!;
@@ -35,6 +34,7 @@ fn save_world(state: *server_state) (void | fs::error) = {
buf[3*i+2] = ((px) &0xff): u8;
};
io::writeall(file, buf)?;
+
};
};