fix menu selection bugs, start adding network protocol, start adding options menu

This commit is contained in:
wan-may 2023-09-14 23:21:34 -03:00
parent 4a7be75dfa
commit e0c44b8743
10 changed files with 203 additions and 97 deletions

View File

@ -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 } )

View File

@ -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 )

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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 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 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 )
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 ) )
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 )
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 )

View File

@ -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

View File

@ -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,
}
},
@ -61,3 +72,15 @@ return menu.new{
}
}
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

View File

@ -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

View File

@ -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.