Compare commits

...

2 Commits

Author SHA1 Message Date
wan-may 4a7be75dfa Refactor tests into separate files; server browser; 2023-09-14 00:51:00 -03:00
wan-may e6b9e7aa13 Server browser functionality. 2023-09-13 00:22:31 -03:00
15 changed files with 254 additions and 145 deletions

View File

@ -1,10 +1,33 @@
#get locations of scripts, libraries, engine
SRC_DIR="src"
LIB_DIR="lib"
LOVE_DIR="../../love"
BUILD_DIR="build"
#build LuaJIT for target platform
#build LuaSocket shared library for target platform
#get appropriate copy of love
#zip client scripts into .love file
cp -r $SRC_DIR/ $BUILD_DIR/
cd $BUILD_DIR/
zip -9 -r vision.love
cat ../../love/love.exe vision.love > vision.exe
#zip .love file with LOVE and dependencies (and license!)
#zip server scripts with LuaJIT and dependencies
#zip metaserver with LuaJIT and dependencies
#WINDOWS: make fused client executable
cat ../../love/love.exe vision.love > vision.exe
#LINUX: get official LOVE AppImage

View File

@ -8,4 +8,13 @@ return setmetatable({
["option_pron"] = "Player Pronouns",
["option_tint"] = "Player Colour",
["option_keybinds"] = "Edit Keybindings",
["utf8_error"] = "??? what is this",
["server_browser"] = "Server Browser",
["ip_button"] = "Enter IP:",
["svinfo_name"] = "Server Name",
["svinfo_map"] = "Current Map",
["svinfo_players"] = "In",
["svinfo_capacity"] = "Max",
["svinfo_ip"] = "IP",
["svinfo_port"] = "Port",
}, {__index = function( t, k ) return k end } )

View File

@ -0,0 +1,3 @@
--TODO: check config option for current language, dynamically change it?
local utf8 = assert( require 'utf8' )
return require 'client.assets.strings.english'

View File

@ -11,7 +11,7 @@ return {
chat = "t",
love = "q",
hate = "e",
}
},
serverIP = "192.168.2.15",
serverPort = 51312,
}

View File

@ -3,13 +3,18 @@ local lg = assert( love.graphics )
local server = assert( require 'client.udp' )
local connecting = {}
local time, ip, port = 0
local time, ip, port
function connecting.draw()
lg.print( "Connecting to server:\t"..time, 0, 0, 0 )
lg.setColor( 1,1,1,1 )
lg.print( ("CONNECTING:\nTIME: %1.1fs\nADDRESS: %s:%d"):format(time, ip, port), 0, 0, 0 )
return false
end
function connecting.keypressed(key, code, isrepeat)
if code == "escape" then return scene.browser() end
end
function connecting.update(dt)
time = time + dt
@ -20,8 +25,12 @@ function connecting.update(dt)
end
function connecting:onLoad( params )
params = params or { ip = "127.0.0.0", port = 8 }
local ip, port = params.ip, params.port
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 )
end

View File

@ -41,7 +41,6 @@ function game.disconnect( )
end
function game.onLoad( params )
end
function game.keypressed( key, code, isRepeat )

View File

@ -0,0 +1,39 @@
local packet = assert( require 'shared.packet' )
local ipString = assert( require 'shared.ipstring' )
local utf8 = assert( require 'utf8' )
local r = math.random
local rand = function() return r( 1, 255 ) end
local rs = function()
local t = {}
for i = 1, r( 0, 64 ) do t[i] = rand() end
return string.char( unpack( t ) )
end
local rutf8 = function()
local t = {}
for i = 1, r( 0, 64 ) do t[i] = r( 30, 1234 ) end
local s = utf8.char( unpack( t ) )
return s
end
local randserver = function()
local str = rutf8
return packet.serverInfo{
players = rand(),
capacity = rand(),
ip = ipString.new{ rand(), rand(), rand(), rand() },
port = r( 1, 65535 ),
version = 25,
svname = str(),
map = str(),
}
end
return {
--Simulate getting server info from the metaserver.
getTestServers = function()
packet.get()
for i = 1, r( 1, 128 ) do randserver() end
return packet.deserialise( packet.get() )
end
}

View File

@ -3,94 +3,84 @@ local scene = assert( require 'client.scene' )
local textInput = assert( require 'client.ui.textinput' )
local button = assert( require 'client.ui.button' )
local packet = assert( require 'shared.packet' )
local ipString = assert( require 'shared.ipstring' )
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 font = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 20 )
local test = assert( require 'client.test.browser' )
local function populateTestServers()
packet.get()
packet.serverInfo{
players = 24,
capacity = 255,
ip = ipString.new{ 123, 456, 789, 101 },
port = 51312,
version = 25,
svname = "test server",
map = "testMap_01",
}
packet.serverInfo{
players = 24,
capacity = 255,
ip = ipString.new{ 123, 456, 789, 101 },
port = 51312,
version = 25,
svname = "test server",
map = "testMap_02",
}
packet.serverInfo{
players = 24,
capacity = 255,
ip = ipString.new{ 150, 645, 151, 67 },
port = 51312,
version = 25,
svname = "test server",
map = "testMap_03",
}
packet.serverInfo{
players = 24,
capacity = 255,
ip = ipString.new{ 123, 456, 789, 101 },
port = 51312,
version = 25,
svname = "test server",
map = "testMap_01",
}
return packet.deserialise( packet.get() )
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 function joinServerCallback( button )
if button.ip and button.port then
return browser.joinIP( button.ip, button.port )
end
end
local serverList = {}
serverList.servers = populateTestServers()
serverList.offsets = {
15,
50,
150,
100,
100,
50,
50
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 )
end
local serverList = menu.new{
name = "serverList",
buttons = serverButtons,
fg = nil,
bg = lg.newMesh{
{ 0, 0, 0, 0, 0.4, 0.05, 0.0, 0.1 },
{ 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 },
},
font = font,
subScene = true }
serverList.selected = false
serverList.x = 25
serverList.y = 220
serverList.y = 0
serverList.h = 36
function serverList.draw()
local gs = packet.getString
local x, y, h = serverList.x, serverList.y, serverList.h
local oldFont = lg.getFont()
lg.setFont( font )
for i, svInfo in ipairs( serverList.servers ) do
lg.setColor( 0.7, 0.7, 0.7, 0.7 )
lg.rectangle( "fill", x, y, 800, h, 5, 5 )
x = x + 15
y = y + 9
lg.setColor( 0, 0, 0, 0.7 )
lg.print( svInfo.version, x, y )
lg.print( gs( svInfo.svname ), 50 + x, y )
lg.print( tostring( svInfo.ip ), 200 + x, y )
lg.print( svInfo.port, 300 + x, y )
lg.print( svInfo.players, 400 + x, y )
lg.print( svInfo.capacity, 450 + x, y )
lg.print( gs( svInfo.map ), 500 + x, y )
y = y - 12
x = x - 15
y = y + h + 5
end
lg.setFont( oldFont )
end
function serverList.select()
end
@ -106,15 +96,21 @@ end
local ti = textInput.new{
width = 300,
length = 20,
x = 15,
y = 175
x = 150,
y = 75,
str = "8.8.8.8:1234"
}
browser.selected = false
function browser.draw()
lg.setColor( 1, 1, 1, 1 )
lg.print( "Server Browser", 15, 115 )
ti:draw()
serverList.draw()
lg.setColor( 1, 1, 1, 1 )
lg.setFont( headerFont )
lg.print( strings.server_browser, 15, 15 )
lg.setFont( midFont )
lg.print( strings.ip_button, 15, 85 )
ti:draw()
end
function browser.update( dt )
@ -122,31 +118,43 @@ function browser.update( dt )
end
function browser.onLoad( )
serverList:onLoad()
lg.setColor( 1, 1, 1, 1 )
end
function browser.mousemoved( x, y, dx, dy, istouch )
return serverList.mousemoved( x, y, dx, dy, istouch )
end
function browser.resize( x, y )
return serverList.resize( x, y )
end
function browser.mousepressed(x, y, button, istouch, pressed)
if ti:contains( x, y ) then return ti:enterText( browser.joinIPString ) end
return serverList.mousepressed( x, y, button, istouch, pressed )
end
function browser.joinIPString( s )
--Parse IP address and port from string. If it's valid, join the server.
print( "browser: Attempting to join server", s )
print( "browser: entered IP and port", s )
if not s then return end
ti:clear()
local valid, ip, port
local ip, port = s:match '(%d+%.%d+%.%d+%.%d+)', s:match ':(%d+)'
print( "browser:", "ip:", ip, port )
if valid then return browser.joinIP( ip, port ) end
if ip and port then return browser.joinIP( ip, port ) end
end
function browser.joinIP( ip, port )
print( "Joining server:", ip, port )
return scene.game{ serverIP = ip, serverPort = port }
return scene.loadScene( scene.connecting, { ip = ip, port = port } )
end
function browser.keypressed( key, code, isRepeat )
if code == "escape" then return scene.mainmenu() end
if code == "return" then return ti:enterText( browser.joinIPString ) end
return serverList.keypressed( key, code, isRepeat )
end
scene.browser = browser

View File

@ -16,12 +16,13 @@ function button:new( t )
if t.h then button.h = t.h end
if t.w then button.w = t.w end
if t.x then button.x = t.x end
if t.space then button.space = t.space end
t.x = t.x or button.x
t.y = t.y or button.y
t.w = t.w or button.w
t.h = t.h or button.h
t.text = t.text or ""
t.text = t.text or lg.newText( lg.getFont(), "button" )
t.color = t.color or { 0.5, 0.5, 0.5, 0.5 }
t.callback = t.callback or function() print( "Clicked button:", t.text ) end
t.selected = t.selected or false
@ -47,9 +48,9 @@ function button:draw( )
end
lg.setColor( 0, 0, 0, 0.8 )
lg.print( self.text, self.x + 15, self.y + 6)
lg.draw( self.text, self.x + 15, self.y + 6)
lg.setColor( 0, 0, 0, 0.2 )
lg.print( self.text, self.x + 18, self.y + 8)
lg.draw( self.text, self.x + 18, self.y + 8)
end
return setmetatable( button, { __call = button.new } )

View File

@ -5,47 +5,49 @@ local strings = strings or assert( require 'client.assets.strings.english' )
local button = assert( require 'client.ui.button' )
local menu = assert( require 'client.ui.menu' )
return menu.new(
"mainmenu",
local font = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 48 )
return menu.new{
name = "mainmenu",
buttons = {
{
button{
x = 15, w = lg.getWidth(), y = 115, h = 72,
text = strings.newgame_button,
x = 15, w = lg.getWidth(), y = 115, h = 72, space = 15,
text = lg.newText( font, strings.newgame_button ),
color = { 0.6, 0.6, 0.6, 0.9 },
callback = function() return scene.connecting() end },
callback = function() return scene.connecting{ip = "127.0.0.0", port = 8} end },
button{
text = strings.join_button,
text = lg.newText( font, strings.join_button ),
color = { 0.6, 0.6, 0.6, 0.9 },
callback = function() return scene.browser() end },
button{
text = strings.option_button,
text = lg.newText( font, strings.option_button ),
color = { 0.6, 0.6, 0.6, 0.9 },
callback = function() return scene.options() end },
button{
text = strings.quit_button,
text = lg.newText( font, strings.quit_button ),
color = { 0.6, 0.6, 0.6, 0.9 },
callback = love.event.quit },
},
lg.newMesh{
fg = lg.newMesh{
{ 0, 0, 0, 0, 0.4, 0.1, 0.05, 0.0 },
{ 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.1, 0.03, 0.0 },
},
lg.newMesh{
bg = lg.newMesh{
{ 0, 0, 0, 0, 0.4, 0.05, 0.0, 0.1 },
{ 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 },
},
lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 48 )
)
font = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 48 )
}

View File

@ -10,10 +10,16 @@ local canvas
local wWidth, wHeight
local currentMenu
function menu.new( name, buttons, fg, bg, font )
local t = { buttons = buttons, fg = fg, bg = bg, font = font or lg.getFont() }
scene[name] = t
print( 'scene', scene )
function menu.getSelectedButton()
return currentMenu.buttons[selectedButtonIdx]
end
function menu.new( t )
if t.subScene then setmetatable( t, t )
else
scene[t.name] = t
print( 'scene', scene )
end
getmetatable( t ).__index = menu
return t
end
@ -21,7 +27,7 @@ end
function menu:onLoad()
print( 'loading:', self.name )
currentMenu = self
lg.setFont( self.font )
if self.font then lg.setFont( self.font ) end
return menu.resize( lg.getDimensions() )
end
@ -42,7 +48,7 @@ end
function menu.mousemoved( x, y, dx, dy, istouch )
if not currentMenu then return end
local buttons = currentMenu.buttons
local selectedButton = buttons[selectedButtonIdx or 0]
for id, menuButton in ipairs( buttons ) do
if menuButton:contains( x, y ) then
@ -77,13 +83,13 @@ function menu.mousepressed( x, y, button, istouch, presses )
end
function menu.keypressed( key, code, isrepeat )
assert( currentMenu )
if code == "escape" then
return love.event.quit()
end
local buttons = currentMenu.buttons
if code == "return" and selectedButtonIdx then
@ -117,7 +123,9 @@ function menu:paint()
--bg
lg.setColor( 1, 1, 1, 1 )
lg.draw( self.bg, 0, 0, 0, wWidth, wHeight )
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
@ -127,12 +135,14 @@ function menu:paint()
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 )
lg.draw( fg, 0, 0, 0, wWidth, wHeight )
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
lg.setCanvas()
end

View File

@ -5,45 +5,47 @@ local strings = strings or assert( require 'client.assets.strings.english' )
local button = assert( require 'client.ui.button' )
local menu = assert( require 'client.ui.menu' )
return menu.new(
"options",
local font = lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 18 )
{
return menu.new{
name = "options",
buttons = {
button{
x = 15, y = 115, w = 800, h = 30,
text = strings.mainmenu_button,
x = 15, y = 115, w = 800, h = 30, space = 8,
text = lg.newText( font, strings.mainmenu_button ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = function() return scene.mainmenu() end },
button{
option = 'name',
text = strings.option_name,
text = lg.newText( font, strings.option_name ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = menu.textOption },
button{
option = 'pronoun',
text = strings.option_pron,
text = lg.newText( font, strings.option_pron ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = menu.textOption },
button{
option = 'colour',
text = strings.option_tint,
text = lg.newText( font, strings.option_tint ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = menu.colourOption },
button{
option = 'keybinds',
text = strings.option_keybinds,
text = lg.newText( font, strings.option_keybinds ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = menu.editKeybinds,
}
},
lg.newMesh{
fg = lg.newMesh{
{ 0, 0, 0, 0, 0.4, 0.1, 0.05, 0.0 },
{ 1, 0, 1, 0, 0.8, 0.3, 0.1, 0.8 },
{ 1, 1, 1, 1, 0.7, 0.4, 0.1, 0.8 },
@ -51,12 +53,11 @@ return menu.new(
},
lg.newMesh{
bg = lg.newMesh{
{ 0, 0, 0, 0, 1, 1, 1, 0.01 },
{ 1, 0, 1, 0, 1, 1, 1, 0.1 },
{ 1, 1, 1, 1, 0, 0, 0, 0.1 },
{ 0, 1, 0, 1, 0, 0, 0, 0.01 },
},
}
lg.newFont( "client/assets/fonts/Montserrat-Bold.ttf", 18 )
)
}

View File

@ -28,8 +28,8 @@ textInput.y = 0
function textInput.new( t )
t = t or {}
t.text = lg.newText( font, "")
t.str = ""
t.str = t.str or ""
t.text = lg.newText( font, t.str )
return setmetatable( t, __mt )
end

View File

@ -64,7 +64,6 @@ function server.Start()
udp:settimeout(0)
server.SetIP( socket.dns.toip(socket.dns.gethostname()), 51312 )
assert( udp:setsockname( tostring( svInfo.ip ), svInfo.port ))
server.StartLocalClient()
print( socket.gettime(), "Start." )
repeat
server.Parse( udp:receivefrom() )

View File

@ -127,6 +127,12 @@ function packet.getString( member )
return ffi.string( member, ffi.sizeof( member ) )
end
--Slow!
function packet.luaString( member )
local s = packet.getString( member )
return s:sub( 1, ( s:find( "\0" ) or s:len() ) - 1 )
end
local writeBuffer = buffer.new( 1024 )
function packet.add( struct, data )
local str = ffi.new( struct.ct, data or 0 )