From c74c2a3133ad4d1f03b83021e53fb9e8a67b3914 Mon Sep 17 00:00:00 2001 From: ubq323 Date: Mon, 6 Feb 2023 11:46:31 +0000 Subject: tick timing in main server loop, refactor, start on saving/loading chunks --- server/map.lua | 44 +++++++++++++ server/server.lua | 189 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 166 insertions(+), 67 deletions(-) create mode 100644 server/map.lua (limited to 'server') diff --git a/server/map.lua b/server/map.lua new file mode 100644 index 0000000..845c644 --- /dev/null +++ b/server/map.lua @@ -0,0 +1,44 @@ +local Map = require"common.map".Map +local class = require"common.class" +local worldgen = require"worldgen" + +local MapS = class.extend(Map) +function MapS.obtain(self,cp) + -- obtain chunk at chunkpos cp by any means necessary + -- if already loaded, just return it + -- if available on disk, load that then return it + -- otherwise, generate a new chunk, load it, then return it. + + -- false is not used on serverside. yet. + + local ch = self:chunk(cp) + if ch then return ch end + + local f = io.open(cp:filename(),"r") + if f then + local j = json.decode(f:read("a")) + ch = Chunk:from_packet_data(j) + f:close() + else + ch = worldgen.gen_chunk(cp) + end + + self:add_chunk(ch) + return ch +end +function MapS.save_chunk(self,cp) + -- any attempt to save not-loaded chunks is silently ignored + local ch = self:chunk(cp) + if not ch then return end + + local f = io.open(cp:filename(),"w") + f:write(ch:data_packet()) + f:flush() + f:close() +end +function MapS.save_and_unload(self,cp) + self:save_chunk(cp) + self:remove_chunk(cp) +end + +return {MapS=MapS} diff --git a/server/server.lua b/server/server.lua index 3bb0951..eda6258 100644 --- a/server/server.lua +++ b/server/server.lua @@ -5,8 +5,8 @@ local Chunk = require"common.chunk".Chunk local noise = require"noise" local coords = require"common.coords" local worldgen = require"worldgen" -local Map = require"common.map".Map - +local MapS = require"map".MapS +local posix_time = require"posix.time" math.randomseed(os.time()) local host = enet.host_create("*:8473") @@ -61,81 +61,136 @@ local function player_move_packet(player,x,y) return json.encode{t="move",id=player.id,x=x,y=y} end +local map = MapS:make() -local map = Map:make() -local function get_or_gen_chunk(cp) - local ch = map:chunk(cp) - if not ch then - ch = worldgen.gen_chunk(cp) - map:add_chunk(ch) - end - return ch -end - -while true do - local ev = host:service(100) - if ev then - if ev.type == "connect" then - local player = make_player(ev.peer) - table.insert(playerlist,player) - print("connect",player.peer,player.id) - player.peer:send(player_you_packet(player)) - local central_chunk = get_or_gen_chunk(coords.ChunkPos:make(0,0)) - player.peer:send(central_chunk:data_packet()) +local function handle_ev(ev) + -- handle network event + if ev.type == "connect" then + local player = make_player(ev.peer) + table.insert(playerlist,player) + print("connect",player.peer,player.id) + player.peer:send(player_you_packet(player)) + local central_chunk = map:obtain(coords.ChunkPos:make(0,0)) + player.peer:send(central_chunk:data_packet()) + for i,otherplayer in ipairs(playerlist) do + if otherplayer ~= player then + -- tell new player about each other player + player.peer:send(player_join_packet(otherplayer)) + -- tell each other player about new player + otherplayer.peer:send(player_join_packet(player)) + end + end + elseif ev.type == "disconnect" then + local player, idx = player_by_peer(ev.peer) + if not player then error("sneeze "..ev.peer) end + print("disconnect",player.peer,player.id) + table.remove(playerlist,idx) + for i,otherplayer in ipairs(playerlist) do + otherplayer.peer:send(player_leave_packet(player)) + end + elseif ev.type == "receive" then + local player = player_by_peer(ev.peer) + if not player then error("sneezey "..ev.peer) end + local j = json.decode(ev.data) + local op = j.t + if op == "ppos" then + local x,y = j.x,j.y + player.pos[1] = x + player.pos[2] = y + -- print(player.id,"-->",player.pos[1],player.pos[2]) for i,otherplayer in ipairs(playerlist) do if otherplayer ~= player then - -- tell new player about each other player - player.peer:send(player_join_packet(otherplayer)) - -- tell each other player about new player - otherplayer.peer:send(player_join_packet(player)) + otherplayer.peer:send(player_move_packet(player,x,y)) end end - elseif ev.type == "disconnect" then - local player, idx = player_by_peer(ev.peer) - if not player then error("sneeze "..ev.peer) end - print("disconnect",player.peer,player.id) - table.remove(playerlist,idx) + elseif op == "settile" then + local h = coords.Hex:make(j.q,j.r) + map:set_at(h,j.tile) + -- print(player.id,"settile",h,j.tile) for i,otherplayer in ipairs(playerlist) do - otherplayer.peer:send(player_leave_packet(player)) - end - elseif ev.type == "receive" then - local player = player_by_peer(ev.peer) - if not player then error("sneezey "..ev.peer) end - local j = json.decode(ev.data) - local op = j.t - if op == "ppos" then - local x,y = j.x,j.y - player.pos[1] = x - player.pos[2] = y - -- print(player.id,"-->",player.pos[1],player.pos[2]) - for i,otherplayer in ipairs(playerlist) do - if otherplayer ~= player then - otherplayer.peer:send(player_move_packet(player,x,y)) - end - end - elseif op == "settile" then - local h = coords.Hex:make(j.q,j.r) - map:set_at(h,j.tile) - -- print(player.id,"settile",h,j.tile) - for i,otherplayer in ipairs(playerlist) do - if otherplayer ~= player then - -- same packet structure s2c as c2s - -- when multiple chunks exist and players only get info - -- about stuff near to them, that won't be the case any more - otherplayer.peer:send(ev.data) - end + if otherplayer ~= player then + -- same packet structure s2c as c2s + -- when multiple chunks exist and players only get info + -- about stuff near to them, that won't be the case any more + otherplayer.peer:send(ev.data) end - elseif op == "reqchunk" then - -- i am not certain this is the best way for this to work - -- i might change it later - local cp = coords.ChunkPos:make(j.u,j.v) - local ch = get_or_gen_chunk(cp) - player.peer:send(ch:data_packet()) end + elseif op == "reqchunk" then + -- i am not certain this is the best way for this to work + -- i might change it later + local cp = coords.ChunkPos:make(j.u,j.v) + local ch = map:obtain(cp) + player.peer:send(ch:data_packet()) + end + end +end +local function timenow() + -- monotonic clock, guaranteed to never go backwards + local tv = posix_time.clock_gettime(posix_time.CLOCK_MONOTONIC) + -- this discards some precision but i don't care + return tv.tv_sec + (tv.tv_nsec/1000000000) +end +-- do something only every x seconds +-- returns function(dt) -> bool +-- which returns true if you should, and false otherwise +local function every(interval) + local time_since = interval+100 + return function(dt) + time_since = time_since + dt + if time_since > interval then + time_since = 0 + return true + else + return false end - -- for k,v in pairs(ev) do io.write(tostring(k),":",tostring(v)," ") end - -- print() + end +end + +local tick_interval = 1 -- seconds +local last_tick_time = timenow() + +-- average tps calculation +local dts = {} +local ndt = 1 +local ndts = 100 +local ntick = 0 + + +local function tick(ntick) + if ntick % 30 == 0 then + print("saving things...") + end +end + +while true do + local now = timenow() + local dt = now - last_tick_time + + if dt >= tick_interval then + dts[ndt] = dt + ndt = 1+(ndt%ndts) + last_tick_time = now + ntick = ntick + 1 + + tick(ntick) + + local tps = 0 + for _,d in ipairs(dts) do tps = tps + 1/d end + tps = tps/#dts + + + print(ndt,ndts) + if 1 == ndt%ndts then + print("tps",tps) + end + end + + local now2 = timenow() + local time_till_next_tick = (last_tick_time+tick_interval)-now2 + if time_till_next_tick > 0 then + local ev = host:service(time_till_next_tick/1000) + if ev then handle_ev(ev) end end end -- cgit v1.2.3