From e77609c5bc8b44aa22ef88063246fd05add5e705 Mon Sep 17 00:00:00 2001
From: ubq323 <ubq323@ubq323.website>
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