diff options
author | ubq323 <ubq323@ubq323.website> | 2025-06-10 10:32:29 +0100 |
---|---|---|
committer | ubq323 <ubq323@ubq323.website> | 2025-06-10 10:32:29 +0100 |
commit | 6f45066de41001ed7c4d7e7b7db4b2e87b7ef3b3 (patch) | |
tree | 7d52bf543e0b3b84a12698932c79d897cb57174a | |
parent | 5189d8102fe68529d463a99a0dc7e0ec5cae22d9 (diff) |
almost done
-rw-r--r-- | client/conf.lua | 11 | ||||
-rw-r--r-- | client/game.lua (renamed from client/main_1.lua) | 50 | ||||
-rw-r--r-- | client/main.lua | 66 | ||||
-rw-r--r-- | client/menu.lua | 106 | ||||
-rw-r--r-- | server/server.lua | 40 |
5 files changed, 172 insertions, 101 deletions
diff --git a/client/conf.lua b/client/conf.lua index dca36d1..1fea49a 100644 --- a/client/conf.lua +++ b/client/conf.lua @@ -1 +1,10 @@ -function love.conf(t) t.window.msaa=8 t.window.title='duet' end +function love.conf(t) + t.window.msaa=8 + t.window.title='duet' + t.identity='duet' + + t.modules.physics = false + t.modules.joystick = false + t.modules.touch = false + t.modules.video = false +end diff --git a/client/main_1.lua b/client/game.lua index f5ee5a9..59c5799 100644 --- a/client/main_1.lua +++ b/client/game.lua @@ -2,6 +2,7 @@ local G = love.graphics local common = require 'common' local Camera = require'r.camera' local Pos = require'r.pos' +local Rect = require'r.rect' local class = require'r.class' local enet = require'enet' local pprint = require'pprint' @@ -13,14 +14,16 @@ local M = {} local SPEED = 8 -- tiles per second local colors = {[true]={141/255,128/255,22/255}, [false]={71/255,50/255,122/255}} -local host = enet.host_create() -local conn = host:connect('localhost:19683') local players = {} local chunks = common.ChunkMap() local lp = { pos=Pos(0,0), movetimer=0, dir=nil } -- local player local cam = Camera(nil, 20) +local host, conn +function M.load(_host,_conn,j) host=_host conn=_conn + lp.pos=Pos(j.pos.x,j.pos.y) lp.name=j.name lp.color=j.color end + local directions = {w=Pos(0,-1),a=Pos(-1,0),s=Pos(0,1),d=Pos(1,0)} function M.keypressed(k,s,r) if directions[s] then @@ -37,16 +40,23 @@ function lp.update(dt) if chunks:tile(newpos) == false then conn:send(json.encode{type='move',pos=newpos}) lp.pos = newpos lp.movetimer = 1 / SPEED end end end -local function draw_player(p, c) G.setColor(c) G.circle('fill',p.x,p.y,0.3) end -function lp.draw() draw_player(lp.pos, {0,1,0}) end +local function draw_player(player,no_label) + G.setColor(player.color) G.circle('fill',player.pos.x,player.pos.y,0.3) + if not no_label then local f = G.getFont() + local txtw,h = f:getWidth(player.name), f:getHeight() + local centre = cam:world_to_screen(player.pos-Pos(0,.4))-Pos(0,h/2) + local bb = Rect:from_centre_dims(centre,txtw,h) + G.push() G.origin() G.setColor(0,0,0,0.8) bb:draw'fill' + G.setColor(1,1,1) G.print(player.name,bb.tl:vals()) G.pop() end end +function lp.draw() draw_player(lp,true) end function M.update(dt) lp.update(dt) - local ev = host:service() while ev do + local ev = host and host:service() while ev do -- pprint(ev) if 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 == 'player' then players[j.name] = {name=j.name,pos=pos} + if j.type == 'player' then players[j.name] = {name=j.name,pos=pos,color=j.color} elseif j.type == 'unplayer' then players[j.from] = nil elseif j.type == 'move' then players[j.from].pos = pos elseif j.type == 'chunk' then chunks:add(pos,{d=rle.decode(j.d)}) @@ -54,7 +64,7 @@ function M.update(dt) elseif j.type == 'tile' then chunks:set_tile(pos,j.tile) end end - ev = host:service() + ev = host and host:service() end end function M.draw() @@ -67,19 +77,19 @@ function M.draw() local t = chunks:tile(Pos(x,y)) if t ~= nil then G.setColor(colors[t~=(x<0)]) G.rectangle('fill',x-0.5,y-0.5,1,1) end end end lp.draw() - for _,player in pairs(players) do draw_player(player.pos,{0,1,1}) end - G.origin() G.setColor(1,0,0) - G.print(tostring(lp.pos),100,100) - G.setColor(0,0,0,0.8) - G.rectangle('fill',0,0,100,100) - G.setColor(1,1,1) - for cx=-7,7 do for cy=-7,7 do local p=Pos(cx,cy) if chunks:get(p) then - p = (p + Pos(7,7))*10 - G.setColor(1,1,1) G.rectangle('fill',p.x,p.y,10,10) - G.setColor(0,0,1) G.rectangle('line',p.x,p.y,10,10) - end end end - local p = (lp.pos/common.SIZE+Pos(7,7))*10 - G.setColor(1,0,0) G.rectangle('fill',p.x-1,p.y-1,2,2) + for _,player in pairs(players) do draw_player(player) end + -- G.origin() G.setColor(1,0,0) + -- G.print(tostring(lp.pos),100,100) + -- G.setColor(0,0,0,0.8) + -- G.rectangle('fill',0,0,100,100) + -- G.setColor(1,1,1) + -- for cx=-7,7 do for cy=-7,7 do local p=Pos(cx,cy) if chunks:get(p) then + -- p = (p + Pos(7,7))*10 + -- G.setColor(1,1,1) G.rectangle('fill',p.x,p.y,10,10) + -- G.setColor(0,0,1) G.rectangle('line',p.x,p.y,10,10) + -- end end end + -- local p = (lp.pos/common.SIZE+Pos(7,7))*10 + -- G.setColor(1,0,0) G.rectangle('fill',p.x-1,p.y-1,2,2) end return M diff --git a/client/main.lua b/client/main.lua index 262d849..64358ab 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,65 +1 @@ -local Pos = require'r.pos' -local Rect = require'r.rect' -local class = require'r.class' -local G = love.graphics -local utf8 = require'utf8' - -local logo = G.newImage"logo.png" logo:setFilter'nearest' - -local function c(...) return {love.math.colorFromBytes(...)} end --- taken from breadquest -local colors = { c(255,64,64), c(255,128,0), c(192,192,64), c(0,192,0), - c(0,192,192), c(64,64,255), c(192,0,192), c(128,128,128)} - -local state = {color=1,focused=nil,name='Susan'} - -local LogoImage = {} -function LogoImage.draw() G.setColor(1,1,1) G.draw(logo, 120, 50, 0, 12) end - -local ColorButton = class() -function ColorButton.make(cls, idx) - local size, gap = 40, 10 - local tl = Pos(100,240) + (idx-1)*Pos(size+gap,0) - local bb = Rect:from_pos(tl,tl+Pos(size,size)) - return setmetatable({bb=bb,idx=idx},cls) end -function ColorButton.draw(self) - G.setColor(colors[self.idx]) self.bb:draw'fill' - if self.idx == state.color then G.setColor(0,0,0) local d=5 - G.line(self.bb.x0+d,self.bb.y0+d,self.bb.x1-d,self.bb.y1-d) - G.line(self.bb.x0+d,self.bb.y1-d,self.bb.x1-d,self.bb.y0+d) end end -function ColorButton.click(self) state.color = self.idx end - -local font = G.newFont(24) -local NameField = class() -function NameField.make(cls,org) - local self = setmetatable({org=org},cls) self:setbb() return self end -function NameField.setbb(self) local width = 12+math.max(200,font:getWidth(state.name)) - self.bb=Rect:from_xywh(self.org.x,self.org.y,width,font:getHeight()) end -function NameField.draw(self) G.setColor(0,0,0) self.bb:draw'line' local d=3 - G.setFont(font) G.print(state.name,self.bb.x0+d,self.bb.y0) - if self == state.focused then local w = 2*d+self.bb.x0+font:getWidth(state.name) - G.line(w,self.bb.y0+d,w,self.bb.y1-d) end end -function NameField.input(self,text) state.name = state.name .. text self:setbb() end -function NameField.key(self,key) if key == 'backspace' and #state.name > 0 then - state.name = state.name:sub(1,utf8.offset(state.name,-1)-1) self:setbb() end end - -local things = { NameField(Pos(100,400)), LogoImage } -for i=1,#colors do table.insert(things, ColorButton(i)) end - -local function draw_ui() for _,thing in ipairs(things) do - if thing.draw then thing:draw() end - if thing.bb and thing.bb:has(Pos(love.mouse.getPosition())) then - G.setColor(0,0,0) thing.bb:draw'line' end end end - -local function which(p) for _,thing in ipairs(things) do - if thing.bb and thing.bb:has(p) then return thing end end end -function love.draw() G.clear(1,1,1) draw_ui() end -function love.mousepressed(x,y,button) - local thing = which(Pos(x,y)) if thing then state.focused = thing - if thing.click then thing:click() end end end -love.keyboard.setKeyRepeat(true) -function love.textinput(text) if state.focused and state.focused.input then - state.focused:input(text) end end -function love.keypressed(key) if state.focused and state.focused.key then - state.focused:key(key) end end - +require'menu' diff --git a/client/menu.lua b/client/menu.lua new file mode 100644 index 0000000..a6eccfb --- /dev/null +++ b/client/menu.lua @@ -0,0 +1,106 @@ +local Pos = require'r.pos' +local Rect = require'r.rect' +local class = require'r.class' +local G = love.graphics +local utf8 = require'utf8' +local enet = require'enet' +local json = require'dkjson' +local pprint = require'pprint' +local qw = require'r.qw' + +local logo = G.newImage"logo.png" logo:setFilter'nearest' + +local function c(...) return {love.math.colorFromBytes(...)} end +-- taken from breadquest +local colors = { c(255,64,64), c(255,128,0), c(192,192,64), c(0,192,0), + c(0,192,192), c(64,64,255), c(192,0,192), c(128,128,128)} + +local state = {color=1,focused=nil,name='Susan',errmsg=nil} +local rstate = love.filesystem.read'menu.json' +if rstate then for k,v in pairs(json.decode(rstate)) do state[k]=v end end + +local LogoImage = {} +function LogoImage.draw() G.setColor(1,1,1) G.draw(logo, 120, 50, 0, 12) end + +local ColorButton = class() +function ColorButton.make(cls, idx) + local size, gap = 40, 10 + local tl = Pos(100,240) + (idx-1)*Pos(size+gap,0) + local bb = Rect:from_pos(tl,tl+Pos(size,size)) + return setmetatable({bb=bb,idx=idx},cls) end +function ColorButton.draw(self) + G.setColor(colors[self.idx]) self.bb:draw'fill' + if self.idx == state.color then G.setColor(0,0,0) local d=5 + G.line(self.bb.x0+d,self.bb.y0+d,self.bb.x1-d,self.bb.y1-d) + G.line(self.bb.x0+d,self.bb.y1-d,self.bb.x1-d,self.bb.y0+d) end end +function ColorButton.click(self) state.color = self.idx end + +local font = G.newFont(24) +local bigfont = G.newFont(36) +local NameField = class() +function NameField.make(cls,org) + local self = setmetatable({org=org},cls) self:setbb() return self end +function NameField.setbb(self) local width = 12+math.max(200,font:getWidth(state.name)) + self.bb=Rect:from_xywh(self.org.x,self.org.y,width,font:getHeight()) end +function NameField.draw(self) G.setColor(0,0,0) self.bb:draw'line' local d=3 + G.setFont(font) G.print(state.name,self.bb.x0+d,self.bb.y0) + if self == state.focused then local w = 2*d+self.bb.x0+font:getWidth(state.name) + G.line(w,self.bb.y0+d,w,self.bb.y1-d) end end +function NameField.input(self,text) state.name = state.name .. text self:setbb() end +function NameField.key(self,key) if key == 'backspace' and #state.name > 0 then + state.name = state.name:sub(1,utf8.offset(state.name,-1)-1) self:setbb() end end + +local w,h = bigfont:getWidth"connecting...", bigfont:getHeight() +local ConnectButton = { bb=Rect:from_xywh(100,400,w,h),c=false } +function ConnectButton.draw(self) G.setColor(0,1,0.2) self.bb:draw'fill' + G.setColor(0,0,0) G.setFont(bigfont) + G.print(self.c and 'connecting...' or 'connect',self.bb.tl:vals()) end + +local ErrDisp = {draw=function() if state.errmsg then + G.setColor(1,0,0) G.setFont(font) G.print(state.errmsg,100,10) end end} + +local things = { NameField(Pos(100,300)), LogoImage, ConnectButton, ErrDisp } +for i=1,#colors do table.insert(things, ColorButton(i)) end + +local function draw_ui() for _,thing in ipairs(things) do + if thing.draw then thing:draw() end + if thing.bb and thing.bb:has(Pos(love.mouse.getPosition())) then + G.setColor(0,0,0) thing.bb:draw'line' end end end + +local function which(p) for _,thing in ipairs(things) do + if thing.bb and thing.bb:has(p) then return thing end end end +function love.draw() G.clear(1,1,1) draw_ui() end +function love.mousepressed(x,y,button) + local thing = which(Pos(x,y)) if thing then state.focused = thing + if thing.click then thing:click() end end end +love.keyboard.setKeyRepeat(true) +function love.textinput(text) if state.focused and state.focused.input then + state.focused:input(text) end end +function love.keypressed(key) if state.focused and state.focused.key then + state.focused:key(key) end end + +local host,conn +function ConnectButton.click(self) + print(love.filesystem.write('menu.json', + json.encode{color=state.color,name=state.name})) + self.c = true host = enet.host_create() conn=host:connect"localhost:19683"end +function ConnectButton.unclick(self) + self.c = false host=nil conn=nil end + +local function switch_scene(modname,...) + local mod = require(modname) + for k in qw.i"update keypressed textinput mousepressed draw" do + love[k] = nil end + for k,v in pairs(mod) do love[k] = v end + G.reset() if mod.load then mod.load(...) end +end + +function love.update(dt) + local ev = host and host:service() while ev do + pprint(ev) + if ev.type == 'receive' then local j = json.decode(ev.data) + if j.type == 'you' then switch_scene('game',host,conn,j) return + elseif j.type == 'error' then print('hi') state.errmsg = j.msg conn:disconnect_later() ConnectButton:unclick() + end + elseif ev.type == 'connect' then conn:send(json.encode{type='hi',name=state.name,color=colors[state.color]}) end + ev = host and host:service() end end diff --git a/server/server.lua b/server/server.lua index a53d9a1..05549bd 100644 --- a/server/server.lua +++ b/server/server.lua @@ -20,9 +20,9 @@ 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) +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(x,y) local fill = nv>0.06 + 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 @@ -41,9 +41,11 @@ function Chunk.obtain(cls,cp) return cls:generate(cp) end local Player = class() -function Player.make(cls, obj) return setmetatable(obj,cls) end +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, type=packet_type } end + 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 @@ -52,8 +54,16 @@ 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 @@ -73,22 +83,22 @@ local function doctor_chunks() 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) end end end + if not used then chunk:save() chunks:remove(cp) end end + if not next(chunks.d) then print'no chunks' end end while true do - local ev = host:service(100) if ev then + 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) - local player = Player{pos=Pos(0,0), peer=peer, name=next_name(), loaded={}} - players[player] = true; greet(player) - elseif ev.type=='disconnect' then - print('disconnecting',peer) - send_others(player, {type='unplayer'}) - players[player] = nil + 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 == 'move' then player.pos=pos send_others(player, j) + 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) end end |