starting over

This commit is contained in:
wan-may 2025-10-26 22:12:53 -03:00
parent 077b075267
commit ddaf61c52c
31 changed files with 4 additions and 1853 deletions

View File

@ -1,21 +0,0 @@
#pragma language glsl3
varying float hue;
uniform float time;
#ifdef VERTEX
uniform mat4 proj;
uniform mat4 view;
uniform sampler2D canvas;
vec4 position(mat4 transform_projection, vec4 vertex_position)
{
return vertex_position;
}
#endif
#ifdef PIXEL
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords)
{
return color;
}
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,68 +0,0 @@
local config
local lfs = assert( love ).filesystem
local cfg = lfs.newFile "cfg.lua"
local mt = { __tostring = function( t )
local arr = { "tblOption{\n ",}
for k, v in pairs( t ) do
arr[#arr + 1] = k
arr[#arr + 1] = " = "
arr[#arr + 1] = tostring( v )
arr[#arr + 1] = ",\n "
end
arr[#arr + 1] = "}"
return table.concat( arr )
end }
local function tblOption(t)
return setmetatable( t, mt )
end
local defaultConfig = {
plName = "Player Name",
plPronoun = tblOption{
subject = "they",
object = "them",
possessive = "their",
},
plColour = tblOption{ 0.5, 0.6, 0.7, 0.5 },
gamma = 0.5,
keybinds = tblOption{
Forward = "w",
Back = "s",
Left = "a",
Right = "d",
Chat = "t",
Love = "q",
Hate = "e",
},
serverIP = "192.168.2.15",
serverPort = 51312,
logFile = "log.txt",
}
local function readConfigFile()
local ok, cfgTbl, err = pcall( lfs.load, 'cfg.lua' )
if ok and cfgTbl then
local ok, err = pcall( cfgTbl )
if err then print( "Failed to load settings file: ", err ) end
return
end
return defaultConfig
end
local function dumpConfigFile()
print( "Saving Settings." )
assert( cfg:open 'w' )
cfg:write( "return {\n " )
for k, v in pairs( config ) do
cfg:write( k )
cfg:write( " = " )
cfg:write( tostring( v ) )
cfg:write( ",\n" )
end
cfg:write( "}" )
end
config = readConfigFile()
return config

View File

@ -1,90 +0,0 @@
local scene = assert( require 'scene' )
local lg = assert( love.graphics )
local server = assert( require 'udp' )
local button = assert( require 'ui.button' )
local strings = assert( require 'strings' )
local connecting = {}
local time, ip, port, attempts, svInfo = 0, 0, 0, 1
local cancelButton = button{
x = lg.getWidth() / 4,
y = lg.getHeight() / 2,
w = lg.getWidth() / 2,
h = 100,
text = lg.newText( lg.getFont(), strings.cancel_button ),
}
function connecting.mousemoved( x, y )
cancelButton.selected = cancelButton:contains( x, y )
end
function connecting.mousepressed( x, y )
if cancelButton:contains( x, y ) then return scene.browser() end
end
function connecting.keypressed( x, y )
return scene.browser()
end
function connecting.draw()
lg.setColor( 1,1,1,1 )
lg.printf( ("ADDRESS: %s"):format( ip ), lg.getWidth() / 4, 15, lg.getWidth() - 30, "left" )
lg.printf( ("ATTEMPTS: %d"):format( attempts ), lg.getWidth() / 4, 20 + lg.getFont():getHeight(), lg.getWidth() - 30, "left" )
cancelButton:draw()
return false
end
function connecting.svChimo( msg )
return server.answerChallenge( msg.nonce )
end
function connecting.connected( msg )
server.setToken( msg.token )
return scene.game()
end
function connecting.keypressed(key, code, isrepeat)
if code == "escape" then return scene.browser() end
end
function connecting.read( msg )
if not msg then return false end
local msgs, types = server.deserialise( msg )
for i = 1, #msgs do
( connecting[ types[i] ] or print )( msgs[i], ip, port )
end
return connecting.read( server.receive() )
end
function connecting.update(dt)
time = time + dt
if time > 2 then
time = 0
attempts = attempts + 1
server.connect( ip, port )
--return scene.loadScene( scene.game )
end
return connecting.read( server.receive() )
end
function connecting:onLoad( params )
lg.setCanvas()
time = 0
attempts = 1
params = params or { ip = "8.8.8.8", port = 8 }
ip, port = params.ip, params.port
if server.isValid( ip, port ) then
server.setInfo( params.svInfo )
return server.connect( ip, port )
end
end
function connecting.exit()
end
scene.connecting = connecting
return connecting

View File

@ -1,23 +0,0 @@
local crepuscular = {}
local lg = assert( love.graphics )
local shader = assert( lg.newShader( 'assets/glsl/crepuscular' ))
local scene = assert( require( 'scene' ) )
local rectanglePosition = { }
function crepuscular.draw()
end
function crepuscular.onLoad()
end
function crepuscular.resize()
end
function crepuscular.update()
end
return crepuscular

View File

@ -1,91 +0,0 @@
local lg = assert( love.graphics )
local scene = assert( require 'scene' )
local shared = assert( require 'shared.shared' )
local server = assert( require 'udp' )
local packet = shared.packet
local crepuscular = assert( require 'crepuscular' )
local game = {}
local handlers = setmetatable( {}, {__index = function() return print end } )
local t = 0
local tick = 0
local serverTick = 0
function handlers.connected( data )
serverTick = math.max( data.tick, serverTick )
tick = math.max( serverTick, tick )
end
function handlers.insect( data )
end
function handlers.soleil( data )
end
function handlers.playerChange( data )
end
function handlers.svInfo( data )
if server.svInfo.map ~= data.map then end
return server.setInfo( data )
end
function handlers.chatMessage( msg )
print( msg.cmsg )
end
function game.draw()
lg.setColor( 1, 1, 1, 1 )
lg.print( "Client Tick: "..tick, 0, 0 )
lg.print( "Server Tick: "..serverTick, 0, 25 )
lg.print( "Current Map: "..packet.getString(server.svInfo.map), 0, 50 )
lg.print( "Server Name: "..packet.getString(server.svInfo.svname), 0, 75 )
lg.print( "# Connected: "..server.svInfo.players, 0, 100 )
end
function game.onPacket( msg )
if not msg or (#msg < 1) then return end
local msgs, types = packet.deserialise( msg )
if not msgs then
print( "malformed packet:", types )
return game.onPacket( server.receive() )
end
for i = 1, #msgs do
--Handler returns something if msg should be discarded.
if handlers[ types[i] ]( msgs[i] ) then break end
end
return game.onPacket( server.receive() )
end
function game.update( dt )
t = dt + t
game.onPacket( server.receive() )
if t > 0.1 then
t = 0
tick = tick + 1
server.newPacket( tick )
assert( server.send() )
end
end
function game.newGame( )
game.curWorld = shared.NewWorld() --Last world state received from server.
game.preWorld = shared.NewWorld() --Current world state predicted by this client.
end
function game.disconnect( )
return scene.mainmenu( server.disconnect( tick ) )
end
function game.onLoad( params )
end
function game.keypressed( key, code, isRepeat )
if code == "escape" then return game.disconnect() end
end
scene.game = game
return game

View File

@ -1,18 +1,17 @@
package.cpath = "..\\?.dll;..\\clib\\?.dll;"..package.cpath
package.path = "..\\?.lua;..\\lualib\\?.lua;"..package.path
local shared = assert( require 'shared.shared' )
local love = assert( love )
function love.load()
package.path = "./client/?.lua"..package.path
print( "Client Started." )
assert( require 'config' )
--Crash if running luajit 2.1 and coconut missing
--[[loadstring( love.data.decode( "string", "base64","G0xKAgrbAQAACgALABY2AAAAOQABADkAAgAnAgMAJwMEADYEAAA5BAEEOQQFBCcGBgA2BwAAOQcHBzkHCAcnCQkAQgcCAEEEAQBBAAICBgAKAFgAAoArAAEAWAEBgCsAAgBMAAIALWQ4MmY3M2RkNjQ1MDcxNDZiNTkwNTMwYjg0NDcwMWZlMmJmYjdjZTkeY2xpZW50L2Fzc2V0cy9jb2NvbnV0LnBuZxFuZXdJbWFnZURhdGEKaW1hZ2UJc2hhMQloYXNoCGhleAtzdHJpbmcLZW5jb2RlCWRhdGEJbG92ZV0BAAUABQAONgAAADMCAQBCAAICDgAAAFgBB4A2AAIANAIAADUDAwA2BAAAPQQEA0IAAwJCAAECMgAAgEwAAgALX19jYWxsAQAAEXNldG1ldGF0YWJsZQAKcGNhbGwA" ))()]]
love.window.setIcon( assert( love.image.newImageData( "assets/client-icon.png" ) ) )
love.graphics.setNewFont( "assets/fonts/Montserrat-Bold.ttf", 48 )
love.window.setIcon( assert( love.image.newImageData( "client/assets/client-icon.png" ) ) )
love.graphics.setNewFont( "client/assets/fonts/Montserrat-Bold.ttf", 48 )
local scenes = assert( require 'scene' )
assert( require 'ui.options' )

View File

@ -1,47 +0,0 @@
local love = assert( love )
local lg = love.graphics
local time = 0
local text = ""
--Set up the canvas.
assert( lg.getCanvasFormats( false ).stencil8, "Error: 8-bit stencil buffers not supported on this system." )
local stencil = love.graphics.newCanvas(
lg.getWidth(),
lg.getHeight(),
{
type = "2d",
format = "stencil8",
readable = false,
msaa = 0,
dpiscale = 1,
mipmaps = "none",
})
local canvas = love.graphics.newCanvas(
lg.getWidth(),
lg.getHeight(),
{
type = "2d",
format = "rgba8",
readable = true,
msaa = 0,
dpiscale = 1,
mipmaps = "none",
})
function love.update(dt)
time = time + dt
end
function love.draw()
lg.setCanvas{{canvas}, depthstencil = {stencil}}
lg.clear()
lg.setColor( 1,1,1,0.3 )
lg.rectangle( "fill", time * 15.0 , 0, 400, 400 )
lg.setCanvas()
lg.draw( canvas )
end
function love.textinput(t)
text = text..t
end

View File

@ -1,47 +0,0 @@
local scene = {}
local love = assert( love )
local callbacks = {
draw = true,
mousepressed = true,
mousemoved = true,
keypressed = true,
update = true,
resize = true,
wheelmoved = true,
}
local mt = {}
function scene.loadScene( scen, params )
print( "Loading Scene:", scen.name )
for k, v in pairs( callbacks ) do
if not scen[k] then print( "Warning: scene missing callback.", scen.name, k ) end
love[k] = scen[k] or love[k]
end
scen:onLoad( params )
end
local function newScene( scenes, name, t )
t.name = t.name or name
print( "Adding Scene:", t.name )
setmetatable( t, { __call = function() return scene.loadScene( t ) end } )
rawset( scenes, name, t )
end
function scene.overlayScene( scen, params )
print( "Adding Scene:", scen.name )
for k, v in pairs( callbacks ) do
local old = love[k]
local new = scen[k]
if new then
love[k] = function( ... ) return new( ... ) and old( ... ) end
end
end
scen:onLoad( params )
end
return setmetatable( scene,
{__call = scene.loadScene,
__newindex = newScene
} )

View File

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

View File

@ -1,39 +0,0 @@
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( 1, 16 ) do t[i] = r( 0x40, 0x70 ) 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, 125 ) do randserver() end
return packet.deserialise( packet.get() )
end
}

View File

@ -1,82 +0,0 @@
local socket = assert( require 'socket' )
local ms = assert( require 'shared.metaserver' )
local config = assert( require 'config' )
local udp = { svInfo = {}, token = false, }
local packet = assert( require 'shared.packet' )
local hash = assert( require 'shared.hash' )
local cxn = assert( socket.udp() )
local mscxn = assert( socket.udp() )
cxn:settimeout( 0 )
mscxn:settimeout( 0 )
assert(mscxn:setpeername( ms.ip, ms.port ))
function udp.receive()
return cxn:receive()
end
udp.deserialise = packet.deserialise
function udp.receiveMeta()
return mscxn:receive()
end
function udp.requestServerList()
print( "Requesting server list." )
packet.get()
packet.metaServer()
packet.clientInfo()
return mscxn:send( packet.get() )
end
function udp.newPacket( tick )
if udp.token then packet.connected{ token = udp.token, tick = tick or 0 } end
end
function udp.setToken( token )
print( "Setting server token:", token )
udp.token = token
end
function udp.setInfo( svInfo )
udp.svInfo = svInfo
end
function udp.answerChallenge( svNonce )
local clNonce = hash.rand()
packet.get()
packet.clChimo{ nonce = clNonce, hash = hash.hash( clNonce, svNonce ) }
print( "Received authentication nonce. Reply:", clNonce, svNonce )
return udp.send()
end
function udp.isValid( ip, port )
local s, e = socket.udp()
if s then s, e = s:setpeername( ip, port ) end
if s then return true
else return nil, e end --temporary socket, gc it, we just want the error
end
function udp.connect( ip, port )
assert( cxn:setpeername( ip, port ) )
print( "Connection request to:", ip, port )
return udp.send( packet.clientInfo{ username = config.plName })
end
function udp.disconnect( tick )
for i = 1, 10 do
udp.newPacket( tick )
packet.disconnect()
udp.send()
end
udp.setToken()
cxn = assert( socket.udp() )
cxn:settimeout( 0 )
end
function udp.send()
return cxn:send( packet.get() )
end
return udp

View File

@ -1,247 +0,0 @@
local lg = assert( love.graphics )
local scene = assert( require 'scene' )
local textInput = assert( require 'ui.textinput' )
local button = assert( require 'ui.button' )
local packet = assert( require 'shared.packet' )
local menu = assert( require 'ui.menu' )
local strings = assert( require 'strings' )
local fonts = assert( require 'ui.fonts' )
local metaserver = assert ( require 'udp' )
local utf8 = assert( require 'utf8' )
local browser = { latest = 0, }
local test = assert( require 'test.browser' )
local font = fonts.font
local cw = fonts.font:getWidth( "w" )
local function joinServerCallback( button )
if button.ip and button.port then
return browser.joinIP( button.ip, button.port, button.serverInfo )
end
end
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 = fonts.font:getWidth( "w" )
local text = lg.newText( fonts.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 serverList = menu.new{
name = "serverList",
buttons = {},
fg = lg.newMesh{
{ 0.5, 0, 0.5, 0, 0, 0, 0, 0 },
{ 1, 0, 1, 0, 1, 1, 1, 0.5 },
{ 1, 1, 1, 1, 1, 1, 1, 0.5 },
{ 0.5, 1, 0.5, 1, 0, 0, 0, 0 },
},
bg = lg.newMesh{
{ 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.9 },
},
font = fonts.font,
subScene = true }
serverList.selected = false
serverList.x = 25
serverList.y = 0
serverList.h = 36
serverList.ips = {}
local serverButtons = serverList.buttons
local color = { 1, 0.6, 0.6, 0.1 }
local ti = textInput.new{
width = lg.getWidth(),
length = 20,
x = cw,
y = 35,
h = 55,
str = strings.ip_button,
font = fonts.midFont,
}
function ti:callback() return self:enterText( browser.joinIPString ) end
local headerButtons = {
button{
callback = function() serverList.clear() return serverList.requestServers() end,
text = lg.newText( fonts.midFont, strings.refresh_button ) ,
color = color,
x = cw * 32,
y = 75,
w = 1400,
h = 36
},
button{
callback = function() return scene.mainmenu() end,
text = lg.newText( fonts.midFont, strings.mainmenu_button ) ,
color = color,
x = cw,
y = 75,
w = 1400,
},
ti,
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 ) },
}
for j, headerButton in ipairs( headerButtons ) do
serverButtons[j] = headerButton
end
function serverList.requestServers()
return metaserver.requestServerList()
end
function serverList.clear( )
for i = #headerButtons + 1, #serverButtons do serverButtons[i] = nil end
for ip in pairs( serverList.ips ) do serverList.ips[ip] = nil end
end
function serverList.add( serverInfo )
local y = 27 * ( #serverButtons - #headerButtons ) + 180
local ip = tostring( serverInfo.ip )
serverButtons[ #serverButtons + 1] = button{
space = 0,
x = cw,
w = lg.getWidth(),
y = y,
h = 24,
color = { 0.3 + 0.1 * (#serverButtons % 2), 0.3 + 0.1 * (#serverButtons % 2), 0.8, 0.3 },
callback = joinServerCallback,
serverInfo = serverInfo,
ip = ip,
port = serverInfo.port,
text = serverInfoToText( serverInfo ),
active = ( y < lg.getHeight() )}
serverList.ips[ ip ] = true
return serverList:paint()
end
local metaServerHandlers = setmetatable(
{
default = function() end,
serverInfo = serverList.add,
},
{__index = function( t ) return t.default end })
function serverList.scroll( up )
local minY = 170
local maxY = lg.getHeight() + 40
if #serverButtons > #headerButtons then
if up and serverButtons[ #headerButtons + 1 ].y > minY then return end
if ( not up ) and serverButtons[ #serverButtons ].y < maxY then return end
up = ( 27 / 3 ) * ( up and 1 or -1 )
for i = #headerButtons + 1, #serverButtons do
local sb = serverButtons[i]
sb.y = sb.y + up
sb.active = ( sb.y > minY ) and ( sb.y < maxY )
end
end
return serverList:paint()
end
browser.selected = false
function browser.draw()
lg.setColor( 1, 1, 1, 1 )
serverList.draw()
end
function browser.update( dt )
local p = metaserver.receiveMeta()
if not p then return end
print( "Receiving server list:", p )
local msgs, types = packet.deserialise( p )
for i = 1, #msgs do metaServerHandlers[types[i]]( msgs[i] ) end
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.wheelmoved( x, y )
if y == 0 then return end
return serverList.scroll( ( y > 0 ) )
end
function browser.resize( x, y )
serverList.resize( x, y )
for i, button in ipairs( serverButtons ) do
button.w = x
end
return serverList:paint()
end
function browser.mousepressed(x, y, button, istouch, pressed)
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.
--TODO: there should be two fields, one for IP, one for port.
--Parsing the entered address for the port is possible but more error-prone.
print( "browser: entered IP and port", s )
if not s then return end
ti:clear()
local ip, port = s:match '(%d+%.%d+%.%d+%.%d+)', s:match ':(%d+)'
print( "browser:", "ip:", ip, port )
if ip and port then return browser.joinIP( ip, port ) end
end
function browser.joinIP( ip, port, svInfo )
print( "Joining server:", ip, port )
return scene.loadScene( scene.connecting, { ip = ip, port = port, svInfo = svInfo } )
end
function browser.keypressed( key, code, isRepeat )
local y = serverList.getSelectedButton()
if code == "escape" then return scene.mainmenu() end
if y and code == "down" and y.y > lg.getHeight() - 100 then
for i = 1, 3 do serverList.scroll( false ) end
end
if y and code == "up" and y.y > 180 and y.y < 250 then
for i = 1, 3 do serverList.scroll( true ) end
end
return serverList.keypressed( key, code, isRepeat )
end
scene.browser = browser
return browser

View File

@ -1,58 +0,0 @@
local lg = assert( love.graphics )
local button = {
h = 60,
y = 0,
x = 0,
w = 100,
space = 15,
}
local mt = { __index = button }
function button:new( t )
t = t or {}
if t.y then button.y = t.y end
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 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
if t.active == nil then t.active = true end
button.y = button.y + t.h + button.space
return setmetatable( t, mt )
end
function button:contains( x, y )
local mx, my, Mx, My = self.x, self.y, self.x + self.w, self.y + self.h
return self.active and (x < Mx and x > mx and y > my and y < My)
end
function button:draw( )
if not self.active then return end
lg.setColor( self.color )
lg.rectangle( "fill", self.x, self.y, self.w, self.h, 10)
if self.selected then
lg.setColor( 1, 1, 1, 0.8 )
lg.rectangle( "fill", self.x + 3, self.y + 3, self.w - 6, self.h - 6, 10 )
end
lg.setColor( 0, 0, 0, 0.8 )
lg.draw( self.text, self.x + 15, self.y + self.h / 2 - self.text:getHeight() / 2 )
lg.setColor( 0, 0, 0, 0.2 )
lg.draw( self.text, self.x + 12, self.y + self.h / 2 - self.text:getHeight() / 2 )-- + self.h / 2 )
end
return setmetatable( button, { __call = button.new } )

View File

@ -1,9 +0,0 @@
--References to font objects.
local lgnf = love.graphics.newFont
return {
font = lgnf( "assets/fonts/Montserrat-Bold.ttf", 14 ),
midFont = lgnf( "assets/fonts/Montserrat-Bold.ttf", 24 ),
headerFont = lgnf( "assets/fonts/Montserrat-Bold.ttf", 48 )
}

View File

@ -1,54 +0,0 @@
local lg = assert( love.graphics )
local love = assert( love )
local scene = assert( require 'scene' )
local strings = strings or assert( require 'strings' )
local button = assert( require 'ui.button' )
local menu = assert( require 'ui.menu' )
local font = assert( require 'ui.fonts').headerFont
return menu.new{
name = "mainmenu",
buttons = {
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{ip = "127.0.0.0", port = 8} end },
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 = lg.newText( font, strings.option_button ),
color = { 0.6, 0.6, 0.6, 0.9 },
callback = function() return scene.options() end },
button{
text = lg.newText( font, strings.quit_button ),
color = { 0.6, 0.6, 0.6, 0.9 },
callback = love.event.quit },
},
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 },
},
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 },
},
font = font,
wheelmoved = function( x, y ) return ( y ~= 0 ) and love.keypressed( nil, (y > 0) and "up" or "down" ) end
}

View File

@ -1,153 +0,0 @@
local love = assert( love )
local lg = assert( love.graphics )
local scene = assert( require 'scene' )
local menu = {}
--Static variables.
local selectedButtonIdx
local canvas
local wWidth, wHeight
local currentMenu
function menu.getSelectedButton()
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 )
if t.subScene then setmetatable( t, t )
else
scene[t.name] = t
end
getmetatable( t ).__index = menu
return t
end
function menu:onLoad()
print( 'Loading Menu:', self.name )
currentMenu = self
if self.font then lg.setFont( self.font ) end
return menu.resize( lg.getDimensions() )
end
function menu.update( dt ) end
function menu.resize( w, h )
wWidth, wHeight = w, h
canvas = lg.newCanvas()
return currentMenu:paint()
end
function menu.draw()
lg.setCanvas()
lg.setColor( 1,1,1,1 )
lg.draw( canvas )
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
if selectedButton then selectedButton.selected = false end
menuButton.selected = true
selectedButtonIdx = id
if menuButton ~= selectedButton then
return currentMenu:paint()
else return end
end
end
--deselect button
if selectedButton then
selectedButtonIdx = nil
selectedButton.selected = false
return currentMenu:paint()
end
end
function menu.mousepressed( x, y, button, istouch, presses )
if not selectedButtonIdx and currentMenu then return end
local uiButton = currentMenu.buttons[selectedButtonIdx]
if uiButton:contains( x, y ) then
selectedButtonIdx = nil
uiButton.selected = false
return uiButton:callback()
end
return currentMenu:paint()
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
local button = buttons[selectedButtonIdx]
selectedButtonIdx = nil
button.selected = false
currentMenu:paint()
return button:callback()
end
if #buttons > 0 and (code == "down" or code == "tab" or code == "up") then
repeat
local sbi = (selectedButtonIdx or 1)
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
selectedButtonIdx = sbi
buttons[selectedButtonIdx].selected = true
until buttons[selectedButtonIdx].active --Skip deactivated buttons.
print( "Selected button: ", selectedButtonIdx )
end
return currentMenu:paint()
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
--buttons
for i = #self.buttons, 1, -1 do self.buttons[i]:draw( ) end
--gradient
lg.setColor( 1, 1, 1, 1 )
if self.fg then lg.draw( self.fg, 0, 0, 0, wWidth, wHeight ) end
lg.setCanvas()
end
return menu

View File

@ -1,84 +0,0 @@
local lg = assert( love.graphics )
local love = assert( love )
local scene = assert( require 'scene' )
local strings = strings or assert( require 'strings' )
local button = assert( require 'ui.button' )
local menu = assert( require 'ui.menu' )
local config = assert( require 'config' )
local font = assert( require 'ui.fonts' ).midFont
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 ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = function() return scene.mainmenu() end },
button{
option = 'plName',
text = lg.newText( font, strings.option_name ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption },
button{
option = 'plPronoun',
text = lg.newText( font, strings.option_pron ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption },
button{
option = 'plColour',
text = lg.newText( font, strings.option_tint ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption },
button{
option = 'keybinds',
text = lg.newText( font, strings.option_keybinds ),
color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption,
}
},
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 },
},
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 },
}
}
do
local op = optionsMenu.paint
function optionsMenu:paint()
local selected = self.getSelectedButton()
local optionName = selected and selected.option
self.buttons[1].text:set( optionName and tostring( config[optionName] ) or "")
return op( self )
end
end
return optionsMenu

View File

@ -1,119 +0,0 @@
local love = assert( love )
local lk = assert( love.keyboard )
local lg = assert( love.graphics )
local utf8 = assert( require 'utf8' )
local string = assert( string )
local button = assert( require 'client.ui.button' )
local _lt
local _lkp
local _lmm
local _lmp
local _callback
local textInput = { }
local __mt = { __index = textInput }
local font = lg.getFont()
-- There is only one active text input widget at a time.
-- It takes exclusive control of key input.
local activeWidget
textInput.width = 200
textInput.length = 20
textInput.x = 0
textInput.y = 0
function textInput.new( t )
t = t or {}
t.str = t.str or ""
t.text = lg.newText( t.font or font, t.str )
return setmetatable( t, __mt )
end
function textInput.keypressed(key, code, isRepeat)
if activeWidget and textInput[code] then textInput[code]() end
if _lkp then return _lkp() end
end
function textInput:clear()
self.str = ""
self.text:set( self.str )
end
function textInput:contains(x, y)
local mx, my, Mx, My = self.x, self.y, self.x + self.width, self.y + font:getHeight()
return (x < Mx and x > mx and y > my and y < My)
end
function textInput:draw()
local w = self.text:getWidth()
local h = (self.font or font):getHeight()
lg.setColor( 1, 1, 1, 0.5 )
if w > 1 then lg.rectangle( "fill", self.x, self.y, math.max( w, 30 ), h, 15, 15 ) end
if self.selected then lg.rectangle( "fill", self.x, self.y, self.width, h, 15, 15 ) end
lg.rectangle( "line", self.x - 3, self.y - 3, self.width + 6, h + 6, 15, 15 )
lg.setColor( 0, 0, 0, 1 )
lg.draw( self.text, self.x or 0, self.y or 0)
end
function textInput:getText()
return self.str
end
function textInput.textInput( s )
activeWidget.str = activeWidget.str..s
activeWidget.text:set( activeWidget.str )
if _lkp then return _lkp() end
end
function textInput.backspace( )
local str = activeWidget.str
local byteoffset = utf8.offset(str, -1)
if byteoffset then str = string.sub(str, 1, byteoffset - 1)
else return end
activeWidget.str = str
activeWidget.text:set( activeWidget.str )
end
function textInput:enterText( callback )
_lt = love.textinput
_lkp = love.keypressed
_lmm = love.mousemoved
_lmp = love.mousepressed
_callback = assert( callback )
love.textinput = textInput.textInput
love.keypressed = textInput.keypressed
love.mousepressed = nil
love.mousemoved = nil
self.oldStr = self.str
self.str = ""
self.text:set( self.str )
activeWidget = self
end
local function disable()
activeWidget = nil
love.textinput = _lt
love.keypressed = _lkp
love.mousemoved = _lmm
love.mousepressed = _lmp
end
function textInput.escape()
activeWidget.str = activeWidget.oldStr or ""
activeWidget.text:set( activeWidget.str )
disable()
return _callback()
end
textInput["return"] = function() --unusual decl because return is a keyword
local str = activeWidget.str
disable()
return _callback( str )
end
return textInput

1
src/main.lua Normal file
View File

@ -0,0 +1 @@
if love then require( "client.main" ) else require( "server.main" ) end

View File

@ -1,23 +0,0 @@
local clients = {}
local client = {}
client.__index = client
function client.new()
local c = setmetatable( {}, client )
clients[ c.id ] = c
return c
end
function client:disconnect()
clients[self.id] = nil
end
function client:assignRole( role )
self.role = role
end
function client.connect( ip, port )
end
return client

View File

@ -1,51 +0,0 @@
function love.conf(t)
t.identity = "vision-server" -- The name of the save directory (string)
t.appendidentity = false -- Search files in source directory before save directory (boolean)
t.version = "11.4" -- The LÖVE version this game was made for (string)
t.console = true -- Attach a console (boolean, Windows only)
t.accelerometerjoystick = true -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean)
t.externalstorage = false -- True to save files (and read from the save directory) in external storage on Android (boolean)
t.gammacorrect = false -- Enable gamma-correct rendering, when supported by the system (boolean)
t.audio.mic = false -- Request and use microphone capabilities in Android (boolean)
t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only)
t.window.title = "vision-server" -- The window title (string)
t.window.icon = false -- Filepath to an image to use as the window's icon (string)
t.window.width = 800 -- The window width (number)
t.window.height = 600 -- The window height (number)
t.window.borderless = false -- Remove all border visuals from the window (boolean)
t.window.resizable = true -- Let the window be user-resizable (boolean)
t.window.minwidth = 400 -- Minimum window width if the window is resizable (number)
t.window.minheight = 400 -- Minimum window height if the window is resizable (number)
t.window.fullscreen = false -- Enable fullscreen (boolean)
t.window.fullscreentype = "desktop" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string)
t.window.vsync = 1 -- Vertical sync mode (number)
t.window.msaa = 3 -- The number of samples to use with multi-sampled antialiasing (number)
t.window.depth = nil -- The number of bits per sample in the depth buffer
t.window.stencil = nil -- The number of bits per sample in the stencil buffer
t.window.display = 1 -- Index of the monitor to show the window in (number)
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
t.window.usedpiscale = true -- Enable automatic DPI scaling when highdpi is set to true as well (boolean)
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
t.modules.audio = true -- Enable the audio module (boolean)
t.modules.data = true -- Enable the data module (boolean)
t.modules.event = true -- Enable the event module (boolean)
t.modules.font = true -- Enable the font module (boolean)
t.modules.graphics = true -- Enable the graphics module (boolean)
t.modules.image = true -- Enable the image module (boolean)
t.modules.joystick = false -- Enable the joystick module (boolean)
t.modules.keyboard = true -- Enable the keyboard module (boolean)
t.modules.math = true -- Enable the math module (boolean)
t.modules.mouse = true -- Enable the mouse module (boolean)
t.modules.physics = true -- Enable the physics module (boolean)
t.modules.sound = true -- Enable the sound module (boolean)
t.modules.system = true -- Enable the system module (boolean)
t.modules.thread = true -- Enable the thread module (boolean)
t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update
t.modules.touch = false -- Enable the touch module (boolean)
t.modules.video = false -- Enable the video module (boolean)
t.modules.window = false -- Enable the window module (boolean)
end

View File

@ -1,212 +0,0 @@
package.cpath = "..\\?.dll;..\\clib\\?.dll;"..package.cpath
package.path = "..\\?.lua;..\\lualib\\?.lua;"..package.path
local shared = assert( require 'shared.shared' )
local packet = shared.packet
local socket = assert( require 'socket' )
local mscxn = assert( socket.udp() )
mscxn:settimeout( 0 )
assert( mscxn:setpeername( shared.metaserver.ip, shared.metaserver.port ), "Could not connect to metaserver!" )
local udp
local io = assert( io )
local CLIENTTIMEOUT = 10
local svInfo = packet.serverInfo{ version = 13,
players = 0,
capacity = 255,
ip = shared.ip.fromString( socket.dns.toip(socket.dns.gethostname()) ),
port = 51312,
svname = "New Server",
map = "Test Map"
}
local clients = {}
local connecting = {}
local server = { tick = 0, time = 0, currentClient = false }
local handlers = setmetatable({
--This is the start of a packet from a connected client.
connected = function( msg, ip, port )
local client = clients[msg.token]
if not client or
(client.ip ~= ip) or
(client.port ~= port)
then
print( "Invalid token from IP:", msg.token, ip, port )
return true
end
if client.tick > msg.tick then
print( "Old packet received:", msg.tick, ip )
return true
end
client.tick = msg.tick
client.time = server.time
server.currentClient = client
end,
--A client wants to disconnect.
disconnect = function( msg, ip, port )
--This is the authenticated client whose packets are being processed.
local client = server.currentClient
if client and client.ip == ip and client.port == port then
print( "Client disconnecting:", client.id)
clients[ client.id ] = nil
server.currentClient = false
return true
end
end,
--Client responds to handshake challenge.
clChimo = function( clChimo, ip, port )
print( "Received handshake response." )
--No active challenge, don't send anything.
local key = ip..":"..port
if not connecting[ key ] then
print( "Old connection attempt from:", key )
return true
end
--Compute session token.
local remoteHash = clChimo.hash
local clNonce = clChimo.nonce
local svNonce = connecting[key].nonce
local token = shared.hash.hash( clNonce, svNonce )
if token ~= remoteHash then
print( "Hashes differ:", shared.hash.hex( clNonce ), shared.hash.hex( svNonce ) )
return true
end
--Hash collision.
while clients[token] do token = token + 1 end
--Successful handshake.
print( "Client connected:", token, port, ip )
clients[ token ] = connecting[ key ]
clients[ token ].id = token
clients[ token ].time = socket.gettime()
packet.connected{ token = token, tick = server.tick }
return udp:sendto( packet.get(), ip, port )
end,
--Metaserver replying with real IP address.
advertised = function( ack, ip, port )
if ip ~= shared.metaserver.ip then return print( "Advertisement acked from rogue address:", ip, port ) end
if udp:getsockname() then
--assert( udp:getsockname() == tostring( ack.ip ), print( tostring( ack.ip ), udp:getsockname()) or "IP mismatch!" )
return print( "Advertised. Address already set." )
end
print( "Advertised. Setting address:", ack.ip, ack.port )
server.SetIP( tostring( ack.ip ), ack.port )
end,
--New client seeks to connect. Challenge immediately.
clientInfo = function( clientInfo, ip, port )
local key = ip..":"..port
connecting[key] = connecting[key] or { ip = ip, port = port, tick = 0 }
local client = connecting[key]
local nonce = shared.hash.rand()
client.nonce = nonce
print( "Received connection request from:", ip, port )
print( "Sending authentication nonce:", nonce )
packet.svChimo{ nonce = nonce }
return udp:sendto( packet.get(), ip, port )
end,
}, {__index = function() return print end })
function server.Advertise()
print( "Advertise." )
packet.get()
packet.metaServer()
packet.serverInfo( svInfo )
mscxn:send( packet.get() )
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) or (#msg < 1) then return end
local msgs, types = packet.deserialise( msg )
if msgs then
for i = 1, #msgs do
if handlers[ types[i] ]( msgs[i], ip, port ) then break end
end
server.currentClient = false
else print( types )
end
return server.Parse( udp:receivefrom() ) -- Process other packets.
end
function server.SetIP( ipString, port )
svInfo.ip = shared.ip.fromString( ipString )
svInfo.port = port
local ok, err = udp:setsockname( ipString, port )
if ok then
print( "Server IP:", udp:getsockname() )
return true
--Find another port.
elseif port < 65536 then
print( "Could not use port:", ipString, port, err )
return server.SetIP( ipString, port + 1 )
else
print( "Could not use IP:", ipString, err )
return error( "Connection failed." )
end
end
function server.Start()
udp = assert( socket.udp() )
udp:settimeout(0)
end
function server.Advance()
server.tick = server.tick + 1
for id, client in pairs( clients ) do
packet.get()
packet.connected{ token = id, tick = server.tick }
udp:sendto( packet.get(), client.ip, client.port )
if server.time - client.time > CLIENTTIMEOUT then
print( "dropping client:", id )
clients[id] = nil
end
end
end
function server.NewGame()
server.tick = 0
end
function server.Quit()
end
server.Start()
function love.update( dt )
server.time = server.time + dt
server.Parse( udp:receivefrom() )
if server.time > 0.1 then
if (server.tick % 50) == 0 then
server.Advertise()
server.Parse( mscxn:receive(), shared.metaserver.ip, shared.metaserver.port )
end
server.time = 0
server.Advance()
end
end
function love.draw() end

View File

@ -1,3 +0,0 @@
local dns = assert( require 'socket' ).dns
--Get your own local IP address via DNS.
return dns.toip( dns.gethostname() )

View File

@ -1,11 +0,0 @@
local math = math
local bit = assert( require 'bit' )
local max = 65536
math.randomseed( 4 )
--hash of a pair of 32-bit numbers
return {
hash = bit.bxor,
hex = bit.tohex,
rand = function()
return math.random( 1, max )
end, }

View File

@ -1,23 +0,0 @@
--CData structure that can hold the longest string representation of an IPv6 address
--we do this because LuaSocket expects a Lua string, and because we can't guarantee v4-only addresses
local ffi = assert( require 'ffi' )
local ipString = {}
local string = assert( string )
ffi.cdef[[
typedef struct {
char ip[40];
} ipAddress;
]]
local ipAddress = ffi.typeof( ffi.new( "ipAddress" ) )
ffi.metatype( ipAddress, { __tostring = function( ip ) return ffi.string( ip.ip, ffi.sizeof( ip.ip ) ) end } )
function ipString.new( t )
local ip = ffi.new( ipAddress )
ip.ip = t
return ip
end
ipString.fromString = ipString.new
return ipString

View File

@ -1,3 +0,0 @@
local ms = { ip = '192.168.2.15', port = 42069 }
print( "Metaserver public address:", ms.ip, ms.port )
return ms

View File

@ -1,216 +0,0 @@
local ffi = assert( require 'ffi' )
local buffer = assert( require( "string.buffer" ) )
local ipString = assert( require 'shared.ipstring' )
local roles = assert( require 'shared.roles' )
local packet = {}
local mt = { __index = { new = function( self ) return ffi.new( self.ct ) end } }
local headerByte = 0x41 --Ensure printable characters at start of packets.
local function newStruct( t )
assert( not( packet[ t.name ] ))
packet[ t.name ] = t
t.netname = headerByte
packet[ headerByte ] = t
headerByte = headerByte + 1
ffi.cdef(("typedef struct {uint8_t netname;\n%s;\n} %s;"):format( table.concat( t, ";\n" ), t.name ))
t.ct = ffi.typeof( ffi.new( t.name ) )
t.size = ffi.sizeof( t.ct )
assert( t.size < 500, t.name )
setmetatable( t, mt )
print( "Packet:", t.name, "Members:", #t + 1, "Size:", t.size, "Alignment:", ffi.alignof( t.ct ) )
end
newStruct{
name = "serverInfo",
"uint8_t players",
"uint8_t capacity",
"ipAddress ip",
"uint16_t version",
"uint16_t port",
"char svname[32]",
"char map[32]",
}
newStruct{
name = "clientInfo",
"char username[31]",
}
newStruct{
name = "svChimo",
"uint32_t nonce",
}
newStruct{
name = "clChimo",
"uint32_t nonce",
"uint32_t hash",
}
newStruct{
name = "connected",
"uint32_t token",
"uint32_t tick",
}
newStruct{
name = "metaServer",
"char padding[300]" --Just a bunch of padding to mitigate amplification.
}
newStruct{
name = "insect",
"uint8_t id",
"bool dead",
"int16_t z",
"int8_t hp",
"int8_t vx",
"int8_t vy",
"int8_t vz",
"int32_t x",
"int32_t y",
}
newStruct{
name = "soleil",
"uint16_t azimuth",
"uint16_t altitude",
"int8_t vazi",
"int8_t valt",
}
newStruct{
name = "playerInfo",
"uint8_t id",
"role_t role",
"char username[31]",
}
newStruct{
name = "chatMessage",
"uint8_t id",
"char cmsg[127]",
}
newStruct{
name = "command",
"char command",
}
newStruct{
name = "advertised",
"uint32_t time",
"ipAddress ip",
"uint16_t port",
}
newStruct{
name = "disconnect",
"char reason",
}
newStruct{
name = "debugLatency",
"uint32_t lastTick",
}
local readBuffer = buffer.new( 1024 )
function packet.deserialise( str )
readBuffer:set( str )
local data = {}
local types = {}
local n = 0
while #readBuffer ~= 0 do
local netname = readBuffer:ref()[0] --Read a byte to determine the packet type.
local t = packet[ netname ]
if not t then
return nil, "Malformed packet. Unknown header:\n"..readBuffer:get()
end
if #readBuffer < t.size then
return nil, "Malformed packet. Packet too small:\n"..readBuffer:get()
end --Malformed packets might cause an overread.
--Allocate new struct and copy into it.
n = n + 1
data[n] = ffi.new( t.ct )
types[n] = t.name
ffi.copy( data[n], readBuffer:ref(), t.size )
readBuffer:skip( t.size )
end
return data, types
end
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 )
str.netname = assert( struct.netname )
writeBuffer:putcdata( str, struct.size )
return str
end
function packet.get()
return writeBuffer:get()
end
mt.__call = packet.add
local testing = testing
--TESTS--
if testing then
packet.serverInfo{
players = 0,
capacity = 255,
map = "abcdefghijklmnopqrstuvwxyz1234567890",
svname = "😘😘😘😘😘😘😘kissyfaceserver",
version = 25,
port = 51312,
ip = ipString.new '132.145.25.62'
}
packet.heartbeat{
tick = 49,
hash = 33753745832876,
protocol = 25
}
packet.insect{
id = 5,
dead = true,
hp = -3,
vx = -5,
vy = 47,
x = 59183,
y = 21412
}
local d, t = packet.deserialise( packet.get() )
assert( #writeBuffer == 0, "Test failed. Write buffer not empty!" )
for i = 1, #d do
print( "", t[i], d[i] )
if t[i] == 'serverInfo' then
print( "", "", packet.getString( d[i].map ), packet.getString( d[i].svname ) )
end
if t[i] == 'insect' then print( d[i].vx ) end
end
packet[42]{}
assert( not( pcall( packet.deserialise, "grgrsgs" ) ), "Test failed. Failed to reject malformed packet." )
end
--END TESTS--
return packet

View File

@ -1,15 +0,0 @@
local _print = print
local lfs = assert( love.filesystem )
local log = assert( lfs.newFile( ("log_%04d.txt"):format( os.time() % 1000 )))
assert( log:open 'a', "Could not open log file!" )
return function( ... )
log:write( os.date("!%M%S") )
for i, v in ipairs{...} do
log:write("\t")
log:write(tostring(v))
end
log:write("\n")
return _print( ... )
end

View File

@ -1,9 +0,0 @@
local ffi = require 'ffi'
ffi.cdef[[
typedef enum {
roleSpectator = 0,
roleSoleil = 1,
roleInsect = 2,
} role_t;
]]
return ffi.typeof( ffi.new 'role_t' )

View File

@ -1,48 +0,0 @@
print( "Loading Shared." )
local shared = {}
--[[do
local rq = require
require = function( ... )
print( "Require:", ... )
return rq( ... )
end
end]]
shared.hash = assert( require 'shared.hash' )
shared.ip = assert( require 'shared.ipstring' )
shared.packet = assert( require 'shared.packet' )
shared.print = assert( require 'shared.print' )
shared.metaserver = assert( require 'shared.metaserver' )
shared.myip = assert( require 'shared.getip' )
--Turn on logging?
print = shared.print
print( "My IP: ", shared.myip )
--World state.
local world = {}
function world:Advance()
end
function world:Reset()
end
function world:Load( map )
end
function world:AddPlayer( playerID, x, y, stage )
end
function world:RemovePlayer( playerID )
end
function shared.NewWorld()
return setmetatable( {}, {__index = world } )
end
return shared