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 --- .gitignore | 3 ++- client/game.lua | 22 ++++++++++------ common/coords.lua | 5 ---- db.lua | 6 +++++ net.txt | 7 +++-- server/chunk.lua | 37 ++++++++++++++++---------- server/db.lua | 15 +++++++++++ server/player.lua | 31 ++++++++++++++++++++++ server/server.lua | 78 ++++++++++++++++++++++++++----------------------------- 9 files changed, 134 insertions(+), 70 deletions(-) create mode 100644 db.lua create mode 100644 server/db.lua create mode 100644 server/player.lua diff --git a/.gitignore b/.gitignore index 448f390..0f889b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.love -server/enet.so +*.so +server/data/* server/world/* flamegraph.pl trace* diff --git a/client/game.lua b/client/game.lua index fd4ade9..aa2e94c 100644 --- a/client/game.lua +++ b/client/game.lua @@ -122,12 +122,17 @@ local remote_players = {} local function update_local_player(pl,dt) local SPEED = 8*math.sqrt(3) -- 8 hexagonheights per second - if love.keyboard.isDown("lshift") then SPEED = SPEED*2 end - local function kd(code) - if love.keyboard.isScancodeDown(code) then return 1 else return 0 end + if love.keyboard.isDown("lshift") or love.keyboard.isScancodeDown'kpenter' then + SPEED = SPEED*2 end - local dx = kd"d"-kd"a" - local dy = kd"s"-kd"w" + local function kd(codes) + for _,code in ipairs(codes) do + if love.keyboard.isScancodeDown(code) then return 1 end + end + return 0 + end + local dx = kd{"d","kp6"}-kd{"a","kp4"} + local dy = kd{"s","kp5"}-kd{"w","kp8"} if dx == 0 and dy == 0 then return @@ -262,9 +267,7 @@ local function draw() draw_player(pl) end - love.graphics.setColor(1,0,0) - love.graphics.rectangle("fill",0,0,1,1) - + -- mouse position (resp. screen, world, hex) local sm = Pos:make(love.mouse.getPosition()) local wm = camera:screen_to_world(sm) local hm = wm:to_hex() @@ -290,7 +293,9 @@ local function draw() love.graphics.origin() + -- selected tile (temp) util.print_good(tostring(selected_tile), "center",10) + if _G.debugmode and local_player then util.print_good(table.concat({ "ms "..tostring(sm), @@ -307,6 +312,7 @@ local function draw() },"\n"),10,10) end + if show_controls then util.print_good(help_text,"center","center") end diff --git a/common/coords.lua b/common/coords.lua index 420b287..fc4aaa5 100644 --- a/common/coords.lua +++ b/common/coords.lua @@ -161,11 +161,6 @@ function ChunkPos.orth_dist(self,other) local dv = math.abs(self.v-other.v) return math.max(du,dv) end -function ChunkPos.filename(self) - -- filename of chunk with that cp - return "world/c_"..self.u.."_"..self.v..".dat" -end - diff --git a/db.lua b/db.lua new file mode 100644 index 0000000..e87b1b6 --- /dev/null +++ b/db.lua @@ -0,0 +1,6 @@ +local lmdb=require'lmdb' +local env=lmdb.open('data',{maxdbs=16}) +return { + env=env, + txn=function(...) return env.txn_begin(...) end, +} diff --git a/net.txt b/net.txt index 4a81d7d..d5f5ef1 100644 --- a/net.txt +++ b/net.txt @@ -11,6 +11,9 @@ reqchunk {u,v} chat {msg} send chat message msg +handshake {username} + sent on join + s2c: playerinfopart:: {id,x,y,color:[r,g,b]} @@ -26,11 +29,11 @@ leave {id} move {id,x,y} player with id id has moved to (x,y) -chunk {tiles=[array of 128*128 booleans],u,v} +chunk {tiles=[array of 128*128 tiles],u,v} chunk at chunkpos u,v has tile data tiles settile {q,r,tile} - tile at H(q,r) was set to tile (currently boolean) + tile at H(q,r) was set to tile chat {msg,from} recieve chat message msg from player with name from. 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