local enet = require"enet" local json = require"common.dkjson" local chunk = require"common.chunk" local Chunk = require"common.chunk".Chunk local noise = require"noise" local coords = require"common.coords" local worldgen = require"worldgen" local MapS = require"map".MapS local posix_time = require"posix.time" math.randomseed(os.time()) local host = enet.host_create("*:8473") 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 local function player_by_id(id) for i,pl in ipairs(playerlist) do if pl.id == id then return pl, i end end return nil end local function player_by_peer(peer) for i,pl in ipairs(playerlist) do if pl.peer == peer then return pl, i end end return nil end local function random_color() return {math.random(),math.random(),math.random()} end local function make_player(peer) local p = {pos={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[1], y=player.pos[2], color=player.color, } end local function player_join_packet(player) return json.encode{t="join",pl=player_info_part(player)} end local function player_you_packet(player) return json.encode{t="you",pl=player_info_part(player)} end local function player_leave_packet(player) return json.encode{t="leave",id=player.id} end local function player_move_packet(player,x,y) return json.encode{t="move",id=player.id,x=x,y=y} end local map = MapS:make() 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 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 elseif ev.type == "receive" then local player = player_by_peer(ev.peer) if not player then error("sneezey "..ev.peer) end local j = json.decode(ev.data) local op = j.t if op == "ppos" then local x,y = j.x,j.y player.pos[1] = x player.pos[2] = y -- print(player.id,"-->",player.pos[1],player.pos[2]) for i,otherplayer in ipairs(playerlist) do if otherplayer ~= player then otherplayer.peer:send(player_move_packet(player,x,y)) end end elseif op == "settile" then local h = coords.Hex:make(j.q,j.r) map:set_at(h,j.tile) -- print(player.id,"settile",h,j.tile) for i,otherplayer in ipairs(playerlist) do if otherplayer ~= player then -- same packet structure s2c as c2s -- when multiple chunks exist and players only get info -- about stuff near to them, that won't be the case any more otherplayer.peer:send(ev.data) end end elseif op == "reqchunk" then -- i am not certain this is the best way for this to work -- i might change it later local cp = coords.ChunkPos:make(j.u,j.v) local ch = map:obtain(cp) player.peer:send(ch:data_packet()) end end end local function timenow() -- monotonic clock, guaranteed to never go backwards local tv = posix_time.clock_gettime(posix_time.CLOCK_MONOTONIC) -- this discards some precision but i don't care return tv.tv_sec + (tv.tv_nsec/1000000000) end -- do something only every x seconds -- returns function(dt) -> bool -- which returns true if you should, and false otherwise local function every(interval) local time_since = interval+100 return function(dt) time_since = time_since + dt if time_since > interval then time_since = 0 return true else return false end end end local tick_interval = 1 -- seconds local last_tick_time = timenow() -- average tps calculation local dts = {} local ndt = 1 local ndts = 100 local ntick = 0 local function tick(ntick) if ntick % 30 == 0 then print("saving things...") end end while true do local now = timenow() local dt = now - last_tick_time if dt >= tick_interval then dts[ndt] = dt ndt = 1+(ndt%ndts) last_tick_time = now ntick = ntick + 1 tick(ntick) local tps = 0 for _,d in ipairs(dts) do tps = tps + 1/d end tps = tps/#dts print(ndt,ndts) if 1 == ndt%ndts then print("tps",tps) end end local now2 = timenow() local time_till_next_tick = (last_tick_time+tick_interval)-now2 if time_till_next_tick > 0 then local ev = host:service(time_till_next_tick/1000) if ev then handle_ev(ev) end end end