summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorubq323 <ubq323@ubq323.website>2023-02-04 23:03:19 +0000
committerubq323 <ubq323@ubq323.website>2023-02-04 23:03:19 +0000
commit0dc1276df57aa16b4f0eaecf54fb5cd8f00115c6 (patch)
tree0d5672f6f05f56022ed834ad35c1c2b2df52c21c /common
parent1ebd7d9b7b62c8e05d527611a1944ed1a876b890 (diff)
many many optimizations and refactorings; introduction of Map to support multiple chunks, modify worldgen and client drawing to support multiple chunks
Diffstat (limited to 'common')
-rw-r--r--common/chunk.lua23
-rw-r--r--common/class.lua7
-rw-r--r--common/constants.lua4
-rw-r--r--common/coords.lua77
-rw-r--r--common/map.lua51
5 files changed, 140 insertions, 22 deletions
diff --git a/common/chunk.lua b/common/chunk.lua
index 1d3faa6..affdab8 100644
--- a/common/chunk.lua
+++ b/common/chunk.lua
@@ -1,9 +1,8 @@
local json = require"common.dkjson"
local class = require"common.class"
+local coords = require"common.coords"
-local CHUNK_SIZE = 64
-
--- for now tiles shall be booleans
+local CHUNK_SIZE = require"common.constants".CHUNK_SIZE
local function index_ok(offq,offr)
return 0<=offq and 0<=offr and offq<CHUNK_SIZE and offr<CHUNK_SIZE
@@ -17,12 +16,15 @@ local function index(offq,offr)
end
local Chunk = class()
-function Chunk.make(cls,tiles)
- return setmetatable({tiles=tiles},cls)
+function Chunk.make(cls,cp,tiles)
+ return setmetatable({cp=cp,tiles=tiles},cls)
end
function Chunk.at(self,hoffs)
- if not index_ok(hoffs.q,hoffs.r) then return nil end
- return self.tiles[index(hoffs.q,hoffs.r)]
+ return self:_atqr(hoffs.q,hoffs.r)
+end
+function Chunk._atqr(self,q,r)
+ if not index_ok(q,r) then return nil end
+ return self.tiles[index(q,r)]
end
function Chunk.set_at(self,hoffs,tile)
if not index_ok(hoffs.q,hoffs.r) then return end
@@ -30,17 +32,18 @@ function Chunk.set_at(self,hoffs,tile)
end
function Chunk.data_packet(self)
- return json.encode{t="chunk",tiles=self.tiles}
+ return json.encode{t="chunk",tiles=self.tiles,u=self.cp.u,v=self.cp.v}
end
function Chunk.from_packet_data(packet)
-- assuming packet has already been json.decoded
-- since otherwise how would we know it's a chunk packet
- return Chunk:make(packet.tiles)
+
+ local cp = coords.ChunkPos:make(packet.u,packet.v)
+ return Chunk:make(cp,packet.tiles)
end
return {
Chunk=Chunk,
- SIZE=CHUNK_SIZE,
index=index,
index_ok=index_ok,
}
diff --git a/common/class.lua b/common/class.lua
index 37cf7bd..f5cd46e 100644
--- a/common/class.lua
+++ b/common/class.lua
@@ -1,3 +1,9 @@
+-- currently a class is a table T with T.__index = T
+-- then to make an instance of this class, we do setmetatable(instance,T)
+-- this should be fine for anything we wish to do. it is possible we will eventually
+-- split this into two separate tables though, perhaps? i don't see why we would ever
+-- do this though.
+
local function class()
local T = {}
T.__index = T
@@ -13,6 +19,7 @@ local function extend(Base)
end
end
setmetatable(T,{__index=Base})
+ return T
end
return setmetatable({
diff --git a/common/constants.lua b/common/constants.lua
new file mode 100644
index 0000000..bf1bb7a
--- /dev/null
+++ b/common/constants.lua
@@ -0,0 +1,4 @@
+-- to avoid some circular dependencies
+return {
+ CHUNK_SIZE=64,
+}
diff --git a/common/coords.lua b/common/coords.lua
index 351aa53..f0ca877 100644
--- a/common/coords.lua
+++ b/common/coords.lua
@@ -1,4 +1,5 @@
local class = require"common.class"
+local CHUNK_SIZE = require"common.constants".CHUNK_SIZE
-- Hex: q,r,s. invariant that q+r+s=0
-- add, subtract
@@ -13,15 +14,24 @@ local function round(x)
end
end
-local Pos, Hex
+local Pos, Hex, ChunkPos
local SR3 = math.sqrt(3)
-local Hex = class()
-function Hex.make(cls,q,r,s)
+Hex = class()
+function Hex.new(cls)
+ return setmetatable({},cls)
+end
+function Hex.init(self,q,r,s)
s=s or -q-r
assert(q+r+s==0,"hex coord doesn't meet invariant")
- return setmetatable({q=q,r=r,s=s},cls)
+ self.q=q
+ self.r=r
+ self.s=s
+ return self
+end
+function Hex.make(cls,...)
+ return cls:new():init(...)
end
function Hex.round(self)
-- return a new Hex rounded to integer coordinates
@@ -44,20 +54,43 @@ function Hex.round(self)
return Hex:make(rq,rr,rs)
end
-function Hex.to_pos(self)
+function Hex.to_pos(self,into)
+ into = into or Pos:new()
local x = self.q*SR3 + self.r*(SR3/2)
local y = self.r*(3/2)
- return Pos:make(x,y)
+ return into:init(x,y)
end
+function Hex.containing_chunk(self)
+ local u = math.floor(self.q/CHUNK_SIZE)
+ local v = math.floor(self.r/CHUNK_SIZE)
+ return ChunkPos:make(u,v)
+end
+function Hex.chunk_and_offset(self)
+ local cp = self:containing_chunk()
+ local tl = cp:extents()
+ return cp, (self-tl)
+end
+function Hex.offset_in_chunk(self)
+ local cp,offs = self:chunk_and_offset()
+ return offs
+end
function Hex.__add(self,other) return Hex:make(self.q+other.q, self.r+other.r, self.s+other.s) end
function Hex.__sub(self,other) return Hex:make(self.q-other.q, self.r-other.r, self.s-other.s) end
function Hex.__tostring(self) return string.format("H(%.2f,%.2f)",self.q,self.r) end
-
+function Hex.__eq(a,b) return a.q==b.q and a.r==b.r end
Pos = class()
-function Pos.make(cls,x,y)
- return setmetatable({x=x,y=y},cls)
+function Pos.new(cls)
+ return setmetatable({},cls)
+end
+function Pos.init(self,x,y)
+ self.x = x
+ self.y = y
+ return self
+end
+function Pos.make(cls,...)
+ return cls:new():init(...)
end
function Pos.__add(self,other) return Pos:make(self.x+other.x,self.y+other.y) end
function Pos.__sub(self,other) return Pos:make(self.x-other.x,self.y-other.y) end
@@ -74,19 +107,39 @@ function Pos.__div(a,b)
assert(type(b) == "number","can only divide Pos by scalar, and can't divide scalar by Pos")
return a*(1/b)
end
+function Pos.__eq(a,b) return a.x==b.x and a.y==b.y end
function Pos.lensq(self) return self.x^2 + self.y^2 end
function Pos.len(self) return math.sqrt(self:lensq()) end
function Pos.norm(self) return self/self:len() end
function Pos.dot(self,other) return self.x*other.x + self.y*other.y end
-function Pos.to_hex(self)
+function Pos.to_hex(self,into)
+ into = into or Hex:new()
local q = self.x*(SR3/3) - self.y*(1/3)
local r = (2/3)*self.y
- return Hex:make(q,r,-q-r)
+ return into:init(q,r,-q-r)
end
function Pos.__tostring(self) return string.format("(%.2f,%.2f)",self.x,self.y) end
+-- represents coordinates of a chunk
+-- ie pair of integers. the chunk at spawn is C(0,0), the one to the right of that is C(1,0), etc
+ChunkPos = class()
+function ChunkPos.make(cls,u,v)
+ return setmetatable({u=u,v=v},cls)
+end
+function ChunkPos.__add(self,other) return ChunkPos:make(self.u+other.u,self.v+other.v) end
+function ChunkPos.__sub(self,other) return ChunkPos:make(self.u-other.u,self.v-other.v) end
+function ChunkPos.__tostring(self) return string.format("C(%d,%d)",self.u,self.v) end
+function ChunkPos.__eq(a,b) return a.u==b.u and a.v==b.v end
+function ChunkPos.extents(self)
+ -- returns Hex of topleft and bottomright
+ local tlq,tlr = self.u*CHUNK_SIZE, self.v*CHUNK_SIZE
+ local brq,brr = (self.u+1)*CHUNK_SIZE -1, (self.v+1)*CHUNK_SIZE -1
+ return Hex:make(tlq,tlr), Hex:make(brq,brr)
+end
+
+
-return {Hex=Hex,Pos=Pos}
+return {Hex=Hex,Pos=Pos,ChunkPos=ChunkPos}
diff --git a/common/map.lua b/common/map.lua
index fe4b9e0..5bcd09e 100644
--- a/common/map.lua
+++ b/common/map.lua
@@ -3,4 +3,55 @@
-- the specifics of which are then implemented separately for client and server
-- it will probably also do things relating to entities and multiblock things
+local class = require"common.class"
+local chunk = require"common.chunk"
+local CHUNK_SIZE = require"common.constants".CHUNK_SIZE
+local Map = class()
+function Map.make(cls)
+ return setmetatable({chunks={}},cls)
+end
+function Map.add_chunk(self,cp,the_chunk)
+ print("adding chunk",cp)
+ assert(the_chunk.cp == cp,"attempting to add chunk with the wrong cp")
+ if not self.chunks[cp.u] then self.chunks[cp.u] = {} end
+ self.chunks[cp.u][cp.v] = the_chunk
+end
+function Map.unload_chunk(self,cp)
+ if not self.chunks[cp.u] then return end
+ self.chunks[cp.u][cp.v] = nil
+ -- remove list if empty
+ if next(self.chunks[cp.u]) == nil then self.chunks[cp.u] = nil end
+end
+function Map.chunk(self,cp)
+ -- return chunk at chunk coord cp
+ -- if that chunk isn't loaded return nil
+ return self:_chunkuv(cp.u,cp.v)
+end
+function Map._chunkuv(self,u,v)
+ -- same as above but with numbers instead of objects
+ -- to avoid allocations inside loop
+ return self.chunks[u] and self.chunks[u][v]
+end
+function Map.at(self,hpos)
+ -- returns tile at world coord hpos
+ -- if that tile's containing chunk isn't loaded, return nil
+
+ -- not using the methods for doing this, in order to avoid lots of allocations
+ -- inside the main drawing loop
+ local cpu,cpv = math.floor(hpos.q/CHUNK_SIZE),math.floor(hpos.r/CHUNK_SIZE)
+ local hoffq,hoffr = hpos.q-(cpu*CHUNK_SIZE), hpos.r-(cpv*CHUNK_SIZE)
+ local ch = self:_chunkuv(cpu,cpv)
+ if ch == nil then return nil end
+ return ch:_atqr(hoffq,hoffr)
+end
+function Map.set_at(self,hpos,tile)
+ local cp,hoffs = hpos:chunk_and_offset()
+ local ch = self:chunk(cp)
+ -- setting a tile in an unloaded chunk is silently ignored
+ -- this might change one day
+ if ch == nil then return nil end
+ ch:set_at(hoffs,tile)
+end
+
+return {Map=Map}