starting over
This commit is contained in:
parent
077b075267
commit
ddaf61c52c
|
|
@ -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 |
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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' )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
} )
|
||||
|
|
@ -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'
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 } )
|
||||
|
|
@ -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 )
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
if love then require( "client.main" ) else require( "server.main" ) end
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
local dns = assert( require 'socket' ).dns
|
||||
--Get your own local IP address via DNS.
|
||||
return dns.toip( dns.gethostname() )
|
||||
|
|
@ -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, }
|
||||
|
|
@ -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
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
local ms = { ip = '192.168.2.15', port = 42069 }
|
||||
print( "Metaserver public address:", ms.ip, ms.port )
|
||||
return ms
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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' )
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue