From e0c44b874310717345cb7f6c5b9410282839c32f Mon Sep 17 00:00:00 2001 From: wan-may Date: Thu, 14 Sep 2023 23:21:34 -0300 Subject: [PATCH] fix menu selection bugs, start adding network protocol, start adding options menu --- src/client/assets/strings/english.lua | 3 +- src/client/connecting.lua | 2 - src/client/game.lua | 15 ++- src/client/test/browser.lua | 4 +- src/client/udp.lua | 3 + src/client/ui/browser.lua | 149 +++++++++++++++++--------- src/client/ui/menu.lua | 44 +++++--- src/client/ui/options.lua | 35 ++++-- src/server.lua | 41 ++++--- src/shared/packet.lua | 4 +- 10 files changed, 203 insertions(+), 97 deletions(-) diff --git a/src/client/assets/strings/english.lua b/src/client/assets/strings/english.lua index ae0f0b0..38058de 100644 --- a/src/client/assets/strings/english.lua +++ b/src/client/assets/strings/english.lua @@ -8,7 +8,7 @@ return setmetatable({ ["option_pron"] = "Player Pronouns", ["option_tint"] = "Player Colour", ["option_keybinds"] = "Edit Keybindings", - ["utf8_error"] = "??? what is this", + ["utf8_error"] = "[countrypilled]", ["server_browser"] = "Server Browser", ["ip_button"] = "Enter IP:", ["svinfo_name"] = "Server Name", @@ -17,4 +17,5 @@ return setmetatable({ ["svinfo_capacity"] = "Max", ["svinfo_ip"] = "IP", ["svinfo_port"] = "Port", + ["refresh_button"] = "Refresh", }, {__index = function( t, k ) return k end } ) \ No newline at end of file diff --git a/src/client/connecting.lua b/src/client/connecting.lua index e7b2ceb..2e3546b 100644 --- a/src/client/connecting.lua +++ b/src/client/connecting.lua @@ -27,8 +27,6 @@ end function connecting:onLoad( params ) lg.setCanvas() time = 0 - print( "connecting:", params, self.ip, self.port ) - for k, v in pairs( self ) do print( k,v ) end params = params or { ip = "8.8.8.8", port = 8 } ip, port = params.ip, params.port return server.connect( ip, port ) diff --git a/src/client/game.lua b/src/client/game.lua index 4c0a1c0..496b004 100644 --- a/src/client/game.lua +++ b/src/client/game.lua @@ -2,6 +2,7 @@ local lg = assert( love.graphics ) local scene = assert( require 'client.scene' ) local shared = assert( require 'shared' ) local server = assert( require 'client.udp' ) +local packet = shared.packet local crepuscular = assert( require 'client.crepuscular' ) local game = {} @@ -15,10 +16,14 @@ function game.draw() lg.print( serverTick, 0, 25 ) end -function game.onPacket( data ) - if not data then return end - serverTick = data - return game.onPacket( server.receive() ) --Recurse to get all waiting packets. +function game.onPacket( msg ) + if not msg then return end + local msgs, types = packet.deserialise( msg ) + if not msgs then return game.onPacket( server.receive() ) end + for i = 1, #msgs do + game[ types[i] ]( msgs[i], ip, port ) + end + return game.onPacket( server.receive() ) end function game.update( dt ) @@ -27,7 +32,7 @@ function game.update( dt ) if t > 0.1 then t = 0 tick = tick + 1 - assert( server.send( tostring(tick) ) ) + assert( server.send( packet.get( packet.heartbeat{ tick = tick, hash = 1234 } ) ) ) end end diff --git a/src/client/test/browser.lua b/src/client/test/browser.lua index 0dd317b..4b30405 100644 --- a/src/client/test/browser.lua +++ b/src/client/test/browser.lua @@ -10,7 +10,7 @@ local rs = function() end local rutf8 = function() local t = {} - for i = 1, r( 0, 64 ) do t[i] = r( 30, 1234 ) end + for i = 1, r( 1, 16 ) do t[i] = r( 0x40, 0x70 ) end local s = utf8.char( unpack( t ) ) return s @@ -33,7 +33,7 @@ return { --Simulate getting server info from the metaserver. getTestServers = function() packet.get() - for i = 1, r( 1, 128 ) do randserver() end + for i = 1, r( 1, 13 ) do randserver() end return packet.deserialise( packet.get() ) end } \ No newline at end of file diff --git a/src/client/udp.lua b/src/client/udp.lua index 929b13b..3d73d40 100644 --- a/src/client/udp.lua +++ b/src/client/udp.lua @@ -1,6 +1,7 @@ local socket = assert( require 'socket' ) local udp = {} +local packet = assert( require 'shared.packet' ) local cxn = assert( socket.udp() ) cxn:settimeout( 0 ) @@ -11,6 +12,7 @@ end function udp.connect( ip, port ) assert( cxn:setpeername( ip, port ) ) + return udp.send( packet.get( packet.heartbeat{ tick = 0, hash = 1234 } ) ) end function udp.disconnect( ) @@ -18,6 +20,7 @@ function udp.disconnect( ) end function udp.send( s ) + print( "udp out:", s ) return cxn:send( s ) end diff --git a/src/client/ui/browser.lua b/src/client/ui/browser.lua index 6787398..8a17850 100644 --- a/src/client/ui/browser.lua +++ b/src/client/ui/browser.lua @@ -6,72 +6,68 @@ local packet = assert( require 'shared.packet' ) local menu = assert( require 'client.ui.menu' ) local strings = assert( require 'client.assets.strings.strings' ) local utf8 = assert( require 'utf8' ) -local gs = function( d ) - local s = packet.getString( d ) - local valid, trip = utf8.len( s ) - return valid and s or --Regurgitate name. - ( trip > 1 ) and s:sub( 1, trip - 1 ) or --Attempt truncation to initial valid utf8 sequence. - strings.utf8_error --Return fallback string indicating garbled mess. -end + local browser = {} local test = assert( require 'client.test.browser' ) + local font = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 12 ) local headerFont = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 36 ) local midFont = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 24 ) +local cw = font:getWidth( "w" ) + local function joinServerCallback( button ) if button.ip and button.port then return browser.joinIP( button.ip, button.port ) end end -local serverButtons = test.getTestServers() -do - local cw = font:getWidth( "w" ) - - - local headerText = lg.newText( font ) - headerText:add( strings.svinfo_name, 0 ) - headerText:add( strings.svinfo_map, cw * 16 ) - headerText:add( strings.svinfo_ip, cw * 32 ) - headerText:add( strings.svinfo_port, cw * ( 32 + 12 ) ) - headerText:add( strings.svinfo_players, cw * ( 32 + 12 + 6 ) ) - headerText:add( strings.svinfo_capacity, cw * ( 32 + 12 + 9 ) ) - local headerButton = button{ - space = 15, y = 135, text = headerText - } - - for i, server in ipairs( serverButtons ) do - local text = lg.newText( font ) - text:add( gs( server.svname ), 0 ) - text:add( gs( server.map ), cw * 16 ) - text:add( tostring( server.ip ), cw * 32 ) - text:add( server.port, cw * ( 32 + 12 ) ) - text:add( server.players, cw * ( 32 + 12 + 6 ) ) - text:add( server.capacity, cw * ( 32 + 12 + 9 ) ) - - serverButtons[i] = button{ space = 0, - ip = tostring( server.ip ), - port = server.port, - text = text, - callback = joinServerCallback, - color = { 0.3 + 0.1 * (i % 2), 0.3 + 0.1 * (i % 2), 0.8, 0.5 }} - end - - table.insert( serverButtons, 1, headerButton ) - +local function tryAdd( text, d, x ) + local s = packet.getString( d ) + return pcall( text.add, text, s, x ) or text:add( strings.utf8_error, x ) end + +local function serverInfoToText( server ) + local cw = font:getWidth( "w" ) + local text = lg.newText( font ) + tryAdd( text, server.svname, 0 ) + tryAdd( text, server.map, cw * 16 ) + text:add( tostring( server.ip ), cw * 32 ) + text:add( server.port, cw * ( 32 + 12 ) ) + text:add( server.players, cw * ( 32 + 12 + 6 ) ) + text:add( server.capacity, cw * ( 32 + 12 + 9 ) ) + return text +end + + +local serverButtons = {} +local color = { 1, 0.6, 0.6, 0.1 } +local headerButtons = { + button{ x = cw * 53, color = color, y = 135, text = lg.newText( font, strings.svinfo_capacity ) }, + button{ x = cw * 50, color = color, y = 135, text = lg.newText( font, strings.svinfo_players ) }, + button{ x = cw * 44, color = color, y = 135, text = lg.newText( font, strings.svinfo_port ) }, + button{ x = cw * 32, color = color, y = 135, text = lg.newText( font, strings.svinfo_ip ) }, + button{ x = cw * 16, color = color, y = 135, text = lg.newText( font, strings.svinfo_map ) }, + button{ x = cw , color = color, y = 135, text = lg.newText( font, strings.svinfo_name ) }, +} + + local serverList = menu.new{ name = "serverList", buttons = serverButtons, - fg = nil, + fg = lg.newMesh{ + { 0.5, 0, 0.5, 0, 0, 0, 0, 0 }, + { 1, 0, 1, 0, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0.5, 1, 0.5, 1, 0, 0, 0, 0 }, + }, bg = lg.newMesh{ - { 0, 0, 0, 0, 0.4, 0.05, 0.0, 0.1 }, + { 0, 0, 0, 0, 0.4, 0.05, 0.0, 0.9 }, { 1, 0, 1, 0, 0.8, 0.3, 0.1, 0.8 }, { 1, 1, 1, 1, 0.7, 0.4, 0.1, 0.8 }, - { 0, 1, 0, 1, 0.4, 0.05, 0.05, 0.1 }, + { 0, 1, 0, 1, 0.4, 0.05, 0.05, 0.9 }, }, font = font, @@ -81,16 +77,63 @@ serverList.x = 25 serverList.y = 0 serverList.h = 36 -function serverList.select() +function serverList.refresh( serverInfo ) + + local n = 1 + for j, headerButton in ipairs( headerButtons ) do + serverButtons[j] = headerButton + n = n + 1 + end + for i, server in ipairs( serverInfo ) do + local b = serverButtons[n] or button{} + b.space = 0 + b.y = 15 + n * 27 + b.h = 24 + b.color = { 0.3 + 0.1 * (n % 2), 0.3 + 0.1 * (n % 2), 0.8, 0.5 } + b.callback = joinServerCallback + b.serverInfo = server + b.ip = tostring( server.ip ) + b.port = server.port + b.text = serverInfoToText( server ) + serverButtons[n] = b + n = n + 1 + print( "Button: ", i, b.y ) + end + + for i = #headerButtons + #serverInfo + 1, #headerButtons + #serverButtons do serverButtons[i] = nil end + + --for i = #headerButtons, 1, -1 do + -- table.insert( serverButtons, 1, headerButtons[i] ) + --end + return serverList:paint() end -function serverList.up() - serverList.selected = ( serverList.selected or 0 ) - 1 -end +local refreshButton = button{ + callback = serverList.onRefresh, + text = lg.newText( midFont, strings.refresh_button ) , + color = { 1, 1, 1, 0.4 }, + x = 450, + y = 75, + w = 400, + h = 55 + } -function serverList.down() - serverList.selected = ( serverList.selected or 0 ) + 1 +do + local rs = serverList.resize + serverList.resize = function( x, y ) + rs( x, y ) + for i, button in ipairs( serverButtons ) do + button.w = x + end + return serverList:paint() + end + + local ol = serverList.onLoad + function serverList:onLoad() + serverList.refresh( test.getTestServers() ) + return ol( serverList ) + end end local ti = textInput.new{ @@ -110,6 +153,7 @@ function browser.draw() lg.print( strings.server_browser, 15, 15 ) lg.setFont( midFont ) lg.print( strings.ip_button, 15, 85 ) + refreshButton:draw() ti:draw() end @@ -123,6 +167,7 @@ function browser.onLoad( ) end function browser.mousemoved( x, y, dx, dy, istouch ) + refreshButton.selected = refreshButton:contains( x, y ) return serverList.mousemoved( x, y, dx, dy, istouch ) end @@ -131,6 +176,7 @@ function browser.resize( x, y ) end function browser.mousepressed(x, y, button, istouch, pressed) + if refreshButton.selected and refreshButton:contains( x, y ) then return serverList.refresh( test.getTestServers() ) end if ti:contains( x, y ) then return ti:enterText( browser.joinIPString ) end return serverList.mousepressed( x, y, button, istouch, pressed ) end @@ -152,6 +198,7 @@ function browser.joinIP( ip, port ) end function browser.keypressed( key, code, isRepeat ) + if code == "q" then return serverList.refresh( test.getTestServers() ) end if code == "escape" then return scene.mainmenu() end if code == "return" then return ti:enterText( browser.joinIPString ) end return serverList.keypressed( key, code, isRepeat ) diff --git a/src/client/ui/menu.lua b/src/client/ui/menu.lua index 1aa68a7..8ee1cf1 100644 --- a/src/client/ui/menu.lua +++ b/src/client/ui/menu.lua @@ -11,7 +11,19 @@ local wWidth, wHeight local currentMenu function menu.getSelectedButton() - return currentMenu.buttons[selectedButtonIdx] + return selectedButtonIdx and currentMenu.buttons[selectedButtonIdx] +end + +function menu.selectButton( idx ) + local but = currentMenu.buttons[selectedButtonIdx] + if but then but.selected = false end + selectedButtonIdx = idx + but = currentMenu.buttons[idx] + if but then but.selected = true end +end + +function menu.clear() + return lg.clear( canvas ) end function menu.new( t ) @@ -102,14 +114,16 @@ function menu.keypressed( key, code, isrepeat ) if code == "down" or code == "tab" or code == "up" then local sbi = (selectedButtonIdx or 1) - buttons[sbi].selected = false + if buttons[sbi] then buttons[sbi].selected = false end --Increment / decrement sbi = sbi + ((code == "up") and -1 or 1) + if #buttons < 1 then selectedButtonIdx = false; return end if sbi > #buttons then sbi = 1 end if sbi < 1 then sbi = #buttons end --Assign + print( "Selected button: ", sbi ) selectedButtonIdx = sbi buttons[selectedButtonIdx].selected = true end @@ -118,31 +132,31 @@ function menu.keypressed( key, code, isrepeat ) return currentMenu:paint() end +function menu:randomColour() + + local r = math.random + local v = r() * 0.4 + 0.6 + local fg = self.fg + fg:setVertexAttribute( 2, 3, v * (0.7 + 0.2 * r()), v * (0.1 + 0.4 * r()), v * (0.1 * r()), 1 ) + fg:setVertexAttribute( 1, 3, 0.3 * ( 1 - v ), 0, 0, 0.4 * (1 - v) ) + fg:setVertexAttribute( 4, 3, 0.3 * ( 1 - v ), 0, 0, 0.4 * (1 - v) ) + fg:setVertexAttribute( 3, 3, v * (0.7 + 0.2 * r()), v * (0.1 + 0.4 * r()), v * (0.1 * r()), 1 ) +end + function menu:paint() lg.setCanvas( canvas ) --bg lg.setColor( 1, 1, 1, 1 ) - if self.bg then - lg.draw( self.bg, 0, 0, 0, wWidth, wHeight ) - end + if self.bg then lg.draw( self.bg, 0, 0, 0, wWidth, wHeight ) end --buttons for i = #self.buttons, 1, -1 do self.buttons[i]:draw( ) end --gradient lg.setColor( 1, 1, 1, 1 ) - local r = math.random - local v = r() * 0.4 + 0.6 - if self.fg then - local fg = self.fg - fg:setVertexAttribute( 2, 3, v * (0.7 + 0.2 * r()), v * (0.1 + 0.4 * r()), v * (0.1 * r()), 1 ) - fg:setVertexAttribute( 1, 3, 0.3 * ( 1 - v ), 0, 0, 0.4 * (1 - v) ) - fg:setVertexAttribute( 4, 3, 0.3 * ( 1 - v ), 0, 0, 0.4 * (1 - v) ) - fg:setVertexAttribute( 3, 3, v * (0.7 + 0.2 * r()), v * (0.1 + 0.4 * r()), v * (0.1 * r()), 1 ) - lg.draw( fg, 0, 0, 0, wWidth, wHeight ) - end + if self.fg then lg.draw( self.fg, 0, 0, 0, wWidth, wHeight ) end lg.setCanvas() end diff --git a/src/client/ui/options.lua b/src/client/ui/options.lua index 091fcfd..1d0a299 100644 --- a/src/client/ui/options.lua +++ b/src/client/ui/options.lua @@ -4,14 +4,25 @@ local scene = assert( require 'client.scene' ) local strings = strings or assert( require 'client.assets.strings.english' ) local button = assert( require 'client.ui.button' ) local menu = assert( require 'client.ui.menu' ) +local config = assert( require 'client.config' ) local font = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 18 ) -return menu.new{ +local function editSelectedOption( button ) + +end + +local optionsMenu = menu.new{ name = "options", buttons = { + button{ + x = 0.5 * lg.getWidth(), w = 0.45 * lg.getWidth(), h = lg.getHeight(), y = 55, + text = lg.newText( font, "" ), + color = { 0.4, 0.1, 0.1, 0.1 } + }, + button{ x = 15, y = 115, w = 800, h = 30, space = 8, text = lg.newText( font, strings.mainmenu_button ), @@ -22,26 +33,26 @@ return menu.new{ option = 'name', text = lg.newText( font, strings.option_name ), color = { 0.6, 0.6, 0.6, 0.8 }, - callback = menu.textOption }, + callback = editSelectedOption }, button{ option = 'pronoun', text = lg.newText( font, strings.option_pron ), color = { 0.6, 0.6, 0.6, 0.8 }, - callback = menu.textOption }, + callback = editSelectedOption }, button{ option = 'colour', text = lg.newText( font, strings.option_tint ), color = { 0.6, 0.6, 0.6, 0.8 }, - callback = menu.colourOption }, + callback = editSelectedOption }, button{ option = 'keybinds', text = lg.newText( font, strings.option_keybinds ), color = { 0.6, 0.6, 0.6, 0.8 }, - callback = menu.editKeybinds, + callback = editSelectedOption, } }, @@ -60,4 +71,16 @@ return menu.new{ { 0, 1, 0, 1, 0, 0, 0, 0.01 }, } -} \ No newline at end of file +} + +do + local op = optionsMenu.paint + function optionsMenu:paint() + local selected = self.getSelectedButton() + local optionName = selected and selected.option + if optionName then self.buttons[1].text:set( optionName ) end + return op( self ) + end +end + +return optionsMenu \ No newline at end of file diff --git a/src/server.lua b/src/server.lua index f44ea1d..1ec35ac 100644 --- a/src/server.lua +++ b/src/server.lua @@ -13,21 +13,24 @@ local svInfo = packet.serverInfo{ version = 13, map = "Test Map" } -local server = { tick = 0 } +local server = { + tick = 0, + logFile = assert( io.open( "../logs/serverLog.txt", "a" ) ), +} -local msIP, msPort = "127.0.0.0", 8 +local msIP, msPort = socket.dns.toip(socket.dns.gethostname()), 42069 local clients = {} ---[[do +do local _print = print function server.Print(...) - _print( ... ) - server.logFile:write( table.concat({os.date("!%Y-%m-%d %X"), ...}, "\t"), "\n" ) - server.logFile:flush() + local time = os.date("!%Y-%m-%d %X") + _print( time, ... ) + --server.logFile:write( table.concat({ ... }, "\t"), "\n" ) end end -local print = server.Print]] +local print = server.Print --Developer convenience function: start the local client program. @@ -36,21 +39,33 @@ function server.StartLocalClient() end function server.Advertise() - print( socket.gettime(), "Advertise." ) + print( "Advertise." ) packet.metaServer() packet.serverInfo( svInfo ) udp:sendto( packet.get() , msIP, msPort ) end +function server.serverInfo( serverInfo ) + print( packet.getString( serverInfo.svname ) ) +end + +function server.metaServer( ms ) + server.advertising = true +end + --Incoming packet. function server.Parse( msg, ip, port ) if not msg then return end if not clients[ip] then + print( "New IP:", ip, port ) clients[ip] = { ip = ip, port = port } end - - local msgs = packet.deserialise( msg ) - print( "in: ", ip, port, msg, server.tick ) + + local msgs, types = packet.deserialise( msg ) + if msgs then for i = 1, #msgs do + print( "Received: ", types[i], ip, port ); + ( server[ types[i] ] or print )( msgs[i], ip, port ) + end end return server.Parse( udp:receivefrom() ) -- Process other packets. end @@ -64,7 +79,7 @@ function server.Start() udp:settimeout(0) server.SetIP( socket.dns.toip(socket.dns.gethostname()), 51312 ) assert( udp:setsockname( tostring( svInfo.ip ), svInfo.port )) - print( socket.gettime(), "Start." ) + print( "Start." ) repeat server.Parse( udp:receivefrom() ) server.Advance() @@ -77,7 +92,7 @@ end function server.Advance() server.tick = server.tick + 1 for id, client in pairs( clients ) do - udp:sendto( string.format("server: %d client: %d", server.tick, client.tick), client.ip, client.port) + -- end end diff --git a/src/shared/packet.lua b/src/shared/packet.lua index 76f25af..e87e944 100644 --- a/src/shared/packet.lua +++ b/src/shared/packet.lua @@ -106,10 +106,10 @@ function packet.deserialise( str ) local netname = readBuffer:ref()[0] --Read a byte to determine the packet type. local t = packet[ netname ] if not t then - error( "Malformed packet. Unknown header:\n"..readBuffer:get() ) + return nil, "Malformed packet. Unknown header:\n"..readBuffer:get() end if #readBuffer < t.size then - error( "Malformed packet. Packet too small:\n"..readBuffer:get() ) + return nil, "Malformed packet. Packet too small:\n"..readBuffer:get() end --Malformed packets might cause an overread. --Allocate new struct and copy into it.