local enet = require'enet' local json = require'dkjson' local Pos = require'r.pos' local pairs_except = require'r.pairs_except' local class = require'r.class' local rle = require'r.rle' local noise = require'r.noise' local common = require 'common' local SIZE = common.SIZE local host = enet.host_create('*:'..common.port) print(host:get_socket_address()) local n = 0 local function next_name() n=n+1 return 'helen'..n end local function P(amp,scale,seed) return {scale=scale,amp=amp,gen=noise.PerlinNoise(seed)} end local noise_gen = noise.NoiseAgg{ P(0.5,17,3), P(1,10,1), P(0.4,6,2) } local chunks = common.ChunkMap() local Chunk = class() function Chunk.make(cls, cp, d) local self = setmetatable({cp=cp,d=d},cls) chunks:add(cp,self) return self end function Chunk.generate(cls,cp) local offs=cp*SIZE local d={} for x=0,SIZE-1 do for y=0,SIZE-1 do local nv = noise_gen:at(offs.x+x,offs.y+y) local fill = nv>0.06 if math.random(3) == 2 then fill = not fill end d[1+x*SIZE+y] = (cp.x<0) ~= fill end end return cls(cp,d) end function Chunk.packet(self) return json.encode{ pos=self.cp, d=rle.encode(self.d), type='chunk' } end local function filename(cp) return 'chunk'..cp:key()..'.json' end function Chunk.load(cls, cp) local f = io.open(filename(cp),'r') if not f then return nil end local j = json.decode(assert(f:read"a")) f:close() return cls(cp,rle.decode(j.d)) end function Chunk.save(self) local f = io.open(filename(self.cp),'w') print('saving',self.cp) assert(f:write(json.encode{d=rle.encode(self.d)})) f:close() end function Chunk.obtain(cls,cp) if chunks:get(cp) then return chunks:get(cp) end local ch = cls:load(cp) if ch then return ch end return cls:generate(cp) end local Player = class() function Player.make(cls, peer, j) return setmetatable({peer=peer, pos=Pos(0,0), name=j.name, color=j.color, loaded={}}, cls) end function Player.packet(self,packet_type) return json.encode { name=self.name, pos=self.pos, color=self.color, type=packet_type } end local players = {} local function find_player(arg) local k,v = next(arg) for pl in pairs(players) do if pl[k] == v then return pl end end end local function other_players(player) return pairs_except(players, player) end local function send_others(player,data) data.from = player.name local packet = json.encode(data) for player2 in other_players(player) do player2.peer:send(packet) end end local function check(peer,j) -- new player if not j.name or #j.name == 0 then return "please enter a name!" end if #j.name >16 then return "name too long!" end if find_player{name=j.name} then return "name already in use! pick a different one!" end end local function greet(player) print('greeting',player.name,player.peer) players[player] = true player.peer:send(player:packet'you') for player2 in other_players(player) do player.peer:send(player2:packet'player') player2.peer:send(player:packet'player') end end local function doctor_chunks() for player in pairs(players) do local pcp = player.pos:divmod(SIZE) for dx = -1,1 do for dy = -1,1 do local cp = pcp+Pos(dx,dy) if not player.loaded[cp:key()] then local chunk = Chunk:obtain(cp) player.peer:send(chunk:packet()) player.loaded[cp:key()] = true end end end for lcpk in pairs(player.loaded) do local lcp = Pos:unkey(lcpk) if (lcp-pcp):linf() > 2 then player.peer:send(json.encode{type='unchunk',pos=lcp}) player.loaded[lcp:key()] = nil end end end for k,chunk in pairs(chunks.d) do local cp = Pos:unkey(k) local used = false for player in pairs(players) do if player.loaded[k] then used=true end end if not used then chunk:save() chunks:remove(cp) if not next(chunks.d) then print'no chunks' end end end end while true do local ev = host:service(10000) if ev then local peer = ev.peer local player = find_player{peer=peer} if ev.type=='connect' then print('connecting',peer) elseif ev.type=='disconnect' then print('disconnecting',peer) if player then send_others(player, {type='unplayer'}) players[player] = nil end elseif ev.type=='receive' then local j = json.decode(ev.data) local pos if j.pos then pos = Pos(j.pos.x,j.pos.y) end if j.type == 'hi' and not player then local err = check(peer,j) if err then peer:send(json.encode{type="error",msg=err}) peer:disconnect_later() else greet(Player(peer,j)) end elseif j.type == 'move' then player.pos=pos send_others(player, j) elseif j.type == 'tile' then chunks:set_tile(pos,j.tile) send_others(player,j) elseif j.type == 'chat' then send_others(player,j) end end end doctor_chunks() io.flush() end