From e77609c5bc8b44aa22ef88063246fd05add5e705 Mon Sep 17 00:00:00 2001 From: ubq323 Date: Fri, 24 Mar 2023 21:11:10 +0000 Subject: use lmdb for world storage; plus other small things support numpad 8456 for movement in addition to wasd refactor server and add player module update outdated documentation slightly --- server/chunk.lua | 37 ++++++++++++++++---------- server/db.lua | 15 +++++++++++ server/player.lua | 31 ++++++++++++++++++++++ server/server.lua | 78 ++++++++++++++++++++++++++----------------------------- 4 files changed, 107 insertions(+), 54 deletions(-) create mode 100644 server/db.lua create mode 100644 server/player.lua (limited to 'server') diff --git a/server/chunk.lua b/server/chunk.lua index 8e71327..852f1fe 100644 --- a/server/chunk.lua +++ b/server/chunk.lua @@ -1,6 +1,7 @@ local class = require"common.class" local Chunk = require"common.chunk".Chunk local json = require"common.dkjson" +local db = require'db' local ChunkS = class.extend(Chunk) function ChunkS.make(cls,...) @@ -8,31 +9,41 @@ function ChunkS.make(cls,...) self.dirty = false return self end -function ChunkS.load_from_disk(cls,cp) - -- returns nil if not there - local filename = cp:filename() - local f = io.open(filename,"r") - if not f then return nil end - local j = json.decode(f:read("a")) - self = cls:from_packet_data(j) + +function ChunkS.apply_migrations(self) + -- if tile format has changed and format in db isn't up to date any more + -- then perform updates here for i,t in ipairs(self.tiles) do if t == false then self.tiles[i] = 0 elseif t == true then self.tiles[i] = 9 end end - f:close() +end + +function ChunkS.load_from_disk(cls,cp) + -- tries to load from database. returns nil if not there. + local txn,dbi = db.get_db('chunks') + local d = dbi[tostring(cp)] + txn:commit() + + if not d then return nil end + local j = json.decode(d) + + local self = cls:from_packet_data(j) + self:apply_migrations() + return self end + function ChunkS.save_if_dirty(self) if self.dirty then print("saving chunk",self.cp) - local filename = self.cp:filename() - local f = io.open(filename,"w") - f:write(self:data_packet()) - f:flush() - f:close() + local txn,dbi = db.get_db('chunks',true) + dbi[tostring(self.cp)] = self:data_packet() + txn:commit() self.dirty = false end end + function ChunkS.set_at(self,...) Chunk.set_at(self,...) self.dirty = true diff --git a/server/db.lua b/server/db.lua new file mode 100644 index 0000000..4208aeb --- /dev/null +++ b/server/db.lua @@ -0,0 +1,15 @@ +local lmdb = require'lmdb' +local env = lmdb.open('data',{maxdbs=16}) +local function get_db(dbname, writeable) + -- shortcut + if writeable == nil then writeable = false end + local txn = assert(env:txn_begin(writeable),"couldn't begin txn") + local the_db = assert(txn:open(dbname), "couldn't open db") + return txn,the_db +end + + +return { + env=env, + get_db=get_db, +} diff --git a/server/player.lua b/server/player.lua new file mode 100644 index 0000000..365eadc --- /dev/null +++ b/server/player.lua @@ -0,0 +1,31 @@ +local class = require'common.class' +local Pos = require'common.coords'.Pos + +local nextid = 1 + +local function random_color() + return {math.random(),math.random(),math.random()} +end + +local Player = class() +function Player.make(cls,peer) + local self = { + pos = Pos:make(0,0), + color = random_color(), + peer = peer, + id = nextid, + } + nextid = nextid + 1 + return setmetatable(self,cls) +end +function Player.info_part(self) + -- eh + return { + id=self.id, + x=self.pos.x, + y=self.pos.y, + color=self.color, + } +end + +return {Player=Player} diff --git a/server/server.lua b/server/server.lua index 24b49ae..2da7988 100644 --- a/server/server.lua +++ b/server/server.lua @@ -6,6 +6,7 @@ local coords = require"common.coords" local Pos = coords.Pos local worldgen = require"worldgen" local MapS = require"map".MapS +local Player=require'player'.Player local posix_time = require"posix.time" local posix_signal = require"posix.signal" @@ -16,7 +17,6 @@ print(host:get_socket_address()) -- sequential list of all players local playerlist = {} -local nextid = 1 -- this is maybe suboptimal -- but it is simplest for now @@ -33,28 +33,12 @@ local function player_by_peer(peer) return nil end -local function random_color() - return {math.random(),math.random(),math.random()} -end -local function make_player(peer) - local p = {pos=Pos:make(0,0),color=random_color(),peer=peer,id=nextid} - nextid = nextid + 1 - return p -end -local function player_info_part(player) - return { - id=player.id, - x=player.pos.x, - y=player.pos.y, - color=player.color, - } -end local function player_join_packet(player) - return json.encode{t="join",pl=player_info_part(player)} + return json.encode{t="join",pl=player:info_part()} end local function player_you_packet(player) - return json.encode{t="you",pl=player_info_part(player)} + return json.encode{t="you",pl=player:info_part()} end local function player_leave_packet(player) return json.encode{t="leave",id=player.id} @@ -68,32 +52,44 @@ end local map = MapS:make() + + +local function on_player_connect(ev) + local player = Player:make(ev.peer) + table.insert(playerlist, player) + + player.peer:send(player_you_packet(player)) + + for i,otherplayer in ipairs(playerlist) do + if otherplayer ~= player then + player.peer:send(player_join_packet(otherplayer)) + otherplayer.peer:send(player_join_packet(player)) + end + end + + print("connect",player.id,player.peer) +end + +local function on_player_disconnect(ev) + local player,idx = player_by_peer(ev.peer) + if not player then error("sneeze"..ev.peer) end + table.remove(playerlist,idx) + + for i,otherplayer in ipairs(playerlist) do + otherplayer.peer:send(player_leave_packet(player)) + end + + print("disconnect", player.id, player.peer) +end + + + 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 + on_player_connect(ev) 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 + on_player_disconnect(ev) elseif ev.type == "receive" then local player = player_by_peer(ev.peer) if not player then error("sneezey "..ev.peer) end -- cgit v1.2.3