Finish moving server and ms back into engine:\n we want the compute shader setup eventually. Hook up options menu for client.

This commit is contained in:
wan-may 2023-09-19 01:31:42 -03:00
parent 1e144775a5
commit 7308086139
20 changed files with 238 additions and 85 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -11,7 +11,7 @@ function love.conf(t)
t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only) t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only)
t.window.title = "vision-dajjal" -- The window title (string) t.window.title = "vision-dajjal" -- The window title (string)
t.window.icon = "client/assets/client-icon.png" -- Filepath to an image to use as the window's icon (string) t.window.icon = "assets/client-icon.png" -- Filepath to an image to use as the window's icon (string)
t.window.width = 800 -- The window width (number) t.window.width = 800 -- The window width (number)
t.window.height = 600 -- The window height (number) t.window.height = 600 -- The window height (number)
t.window.borderless = false -- Remove all border visuals from the window (boolean) t.window.borderless = false -- Remove all border visuals from the window (boolean)

View File

@ -1,31 +1,58 @@
local config = {} 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 = { local defaultConfig = {
plName = "Player Name", plName = "Player Name",
plPronoun = "they/them/their", plPronoun = tblOption{
plR = 0.8, subject = "they",
plG = 0.4, object = "them",
plB = 0.4, possessive = "their",
},
plColour = tblOption{ 0.5, 0.6, 0.7, 0.5 },
gamma = 0.5, gamma = 0.5,
keyForward = "w", keybinds = tblOption{
keyBack = "s", Forward = "w",
keyLeft = "a", Back = "s",
keyRight = "d", Left = "a",
keyChat = "t", Right = "d",
keyLove = "q", Chat = "t",
keyHate = "e", Love = "q",
Hate = "e",
},
serverIP = "192.168.2.15", serverIP = "192.168.2.15",
serverPort = 51312, serverPort = 51312,
logFile = "../logs/log.txt", logFile = "log.txt",
} }
local function readConfigFile() local function readConfigFile()
return dofile( "./cfg.lua" ) or defaultConfig 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 end
local function dumpConfigFile() local function dumpConfigFile()
print( "Saving Settings." ) print( "Saving Settings." )
local cfg = assert(io.open( "./cfg.lua", "w" )) assert( cfg:open 'w' )
cfg:write( "return {\n " ) cfg:write( "return {\n " )
for k, v in pairs( config ) do for k, v in pairs( config ) do
cfg:write( k ) cfg:write( k )
@ -36,4 +63,6 @@ local function dumpConfigFile()
cfg:write( "}" ) cfg:write( "}" )
end end
config = readConfigFile()
return config return config

View File

@ -1,8 +1,8 @@
local scene = assert( require 'client.scene' ) local scene = assert( require 'scene' )
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local server = assert( require 'client.udp' ) local server = assert( require 'udp' )
local button = assert( require 'client.ui.button' ) local button = assert( require 'ui.button' )
local strings = assert( require 'client.strings' ) local strings = assert( require 'strings' )
local connecting = {} local connecting = {}
local time, ip, port, attempts = 0, 0, 0, 0 local time, ip, port, attempts = 0, 0, 0, 0

View File

@ -1,7 +1,7 @@
local crepuscular = {} local crepuscular = {}
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local shader = assert( lg.newShader( 'client/assets/glsl/crepuscular' )) local shader = assert( lg.newShader( 'assets/glsl/crepuscular' ))
local scene = assert( require( 'client.scene' ) ) local scene = assert( require( 'scene' ) )
local rectanglePosition = { } local rectanglePosition = { }
function crepuscular.draw() function crepuscular.draw()

View File

@ -1,9 +1,9 @@
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local scene = assert( require 'client.scene' ) local scene = assert( require 'scene' )
local shared = assert( require 'shared' ) local shared = assert( require 'shared.shared' )
local server = assert( require 'client.udp' ) local server = assert( require 'udp' )
local packet = shared.packet local packet = shared.packet
local crepuscular = assert( require 'client.crepuscular' ) local crepuscular = assert( require 'crepuscular' )
local game = {} local game = {}
local t = 0 local t = 0

View File

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

View File

@ -1,17 +1,17 @@
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local scene = assert( require 'client.scene' ) local scene = assert( require 'scene' )
local textInput = assert( require 'client.ui.textinput' ) local textInput = assert( require 'ui.textinput' )
local button = assert( require 'client.ui.button' ) local button = assert( require 'ui.button' )
local packet = assert( require 'shared.packet' ) local packet = assert( require 'shared.packet' )
local menu = assert( require 'client.ui.menu' ) local menu = assert( require 'ui.menu' )
local strings = assert( require 'client.strings' ) local strings = assert( require 'strings' )
local fonts = assert( require 'client.ui.fonts' ) local fonts = assert( require 'ui.fonts' )
local metaserver = assert ( require 'client.udp' ) local metaserver = assert ( require 'udp' )
local utf8 = assert( require 'utf8' ) local utf8 = assert( require 'utf8' )
local browser = { latest = 0, } local browser = { latest = 0, }
local test = assert( require 'client.test.browser' ) local test = assert( require 'test.browser' )
local font = fonts.font local font = fonts.font
local cw = fonts.font:getWidth( "w" ) local cw = fonts.font:getWidth( "w" )

View File

@ -3,7 +3,7 @@ local lgnf = love.graphics.newFont
return { return {
font = lgnf( "client/assets/fonts/Montserrat-Bold.ttf", 14 ), font = lgnf( "assets/fonts/Montserrat-Bold.ttf", 14 ),
midFont = lgnf( "client/assets/fonts/Montserrat-Bold.ttf", 24 ), midFont = lgnf( "assets/fonts/Montserrat-Bold.ttf", 24 ),
headerFont = lgnf( "client/assets/fonts/Montserrat-Bold.ttf", 48 ) headerFont = lgnf( "assets/fonts/Montserrat-Bold.ttf", 48 )
} }

View File

@ -1,10 +1,10 @@
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local love = assert( love ) local love = assert( love )
local scene = assert( require 'client.scene' ) local scene = assert( require 'scene' )
local strings = strings or assert( require 'client.assets.strings.english' ) local strings = strings or assert( require 'strings' )
local button = assert( require 'client.ui.button' ) local button = assert( require 'ui.button' )
local menu = assert( require 'client.ui.menu' ) local menu = assert( require 'ui.menu' )
local font = assert( require 'client.ui.fonts').headerFont local font = assert( require 'ui.fonts').headerFont
return menu.new{ return menu.new{
name = "mainmenu", name = "mainmenu",

View File

@ -1,6 +1,6 @@
local love = assert( love ) local love = assert( love )
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local scene = assert( require 'client.scene' ) local scene = assert( require 'scene' )
local menu = {} local menu = {}
--Static variables. --Static variables.

View File

@ -1,11 +1,11 @@
local lg = assert( love.graphics ) local lg = assert( love.graphics )
local love = assert( love ) local love = assert( love )
local scene = assert( require 'client.scene' ) local scene = assert( require 'scene' )
local strings = strings or assert( require 'client.assets.strings.english' ) local strings = strings or assert( require 'strings' )
local button = assert( require 'client.ui.button' ) local button = assert( require 'ui.button' )
local menu = assert( require 'client.ui.menu' ) local menu = assert( require 'ui.menu' )
local config = assert( require 'client.config' ) local config = assert( require 'config' )
local font = assert( require 'client.ui.fonts' ).midFont local font = assert( require 'ui.fonts' ).midFont
local function editSelectedOption( button ) local function editSelectedOption( button )
@ -29,19 +29,19 @@ local optionsMenu = menu.new{
callback = function() return scene.mainmenu() end }, callback = function() return scene.mainmenu() end },
button{ button{
option = 'name', option = 'plName',
text = lg.newText( font, strings.option_name ), text = lg.newText( font, strings.option_name ),
color = { 0.6, 0.6, 0.6, 0.8 }, color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption }, callback = editSelectedOption },
button{ button{
option = 'pronoun', option = 'plPronoun',
text = lg.newText( font, strings.option_pron ), text = lg.newText( font, strings.option_pron ),
color = { 0.6, 0.6, 0.6, 0.8 }, color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption }, callback = editSelectedOption },
button{ button{
option = 'colour', option = 'plColour',
text = lg.newText( font, strings.option_tint ), text = lg.newText( font, strings.option_tint ),
color = { 0.6, 0.6, 0.6, 0.8 }, color = { 0.6, 0.6, 0.6, 0.8 },
callback = editSelectedOption }, callback = editSelectedOption },
@ -76,7 +76,7 @@ do
function optionsMenu:paint() function optionsMenu:paint()
local selected = self.getSelectedButton() local selected = self.getSelectedButton()
local optionName = selected and selected.option local optionName = selected and selected.option
if optionName then self.buttons[1].text:set( optionName ) end self.buttons[1].text:set( optionName and tostring( config[optionName] ) or "")
return op( self ) return op( self )
end end
end end

51
src/metaserver/conf.lua Normal file
View File

@ -0,0 +1,51 @@
function love.conf(t)
t.identity = "metaserver" -- 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 = false -- 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 = "metaserver" -- 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 = false -- 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 = false -- 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 = false -- Enable the physics module (boolean)
t.modules.sound = false -- 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 = true -- Enable the window module (boolean)
end

3
src/metaserver/getip.lua Normal file
View File

@ -0,0 +1,3 @@
local http = assert( require 'socket.http' )
print( "Retrieving IP: " )
return assert( http.request 'https://api.ipify.org' )

View File

@ -1,7 +1,7 @@
local SERVERTIMEOUT = 30 local SERVERTIMEOUT = 30
local CLIENTTIMEOUT = 1 local CLIENTTIMEOUT = 1
local shared = assert( require 'shared' ) local shared = assert( require '../shared.shared' )
local socket = assert( require 'socket' ) local socket = assert( require 'socket' )
local packet = assert( shared.packet ) local packet = assert( shared.packet )
local udp = assert( socket.udp() ) local udp = assert( socket.udp() )
@ -84,9 +84,9 @@ local function prune( t )
end end
print( "Starting Metaserver", socket.gettime() ) print( "Starting Metaserver", socket.gettime() )
repeat function love.update()
read( udp:receivefrom() ) read( udp:receivefrom() )
prune( socket.gettime() ) prune( socket.gettime() )
io.flush() io.flush()
tick = tick + 1 tick = tick + 1
until socket.sleep( 2.0 ) end

51
src/server/conf.lua Normal file
View File

@ -0,0 +1,51 @@
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 = true -- Enable the window module (boolean)
end

View File

@ -1,4 +1,6 @@
local shared = assert( require 'shared' ) 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 packet = shared.packet
local socket = assert( require 'socket' ) local socket = assert( require 'socket' )
local ms = shared.metaserver local ms = shared.metaserver
@ -68,6 +70,7 @@ function server.SetIP( ipString, port )
print( ipString, port ) print( ipString, port )
--Find another port. --Find another port.
elseif port < 65536 then elseif port < 65536 then
print( "Trying IP:", ipString, port )
return server.SetIP( ipString, port + 1 ) return server.SetIP( ipString, port + 1 )
else else
print( "Could not use IP: "..ipString ) print( "Could not use IP: "..ipString )
@ -75,18 +78,12 @@ function server.SetIP( ipString, port )
end end
end end
function server.Start() function server.Start()
udp = assert( socket.udp() ) udp = assert( socket.udp() )
udp:settimeout(0) udp:settimeout(0)
server.SetIP( shared.myip, 51312 ) server.SetIP( socket.dns.toip(socket.dns.gethostname()), 51312 )
print( "Server started:", udp:getsockname() ) print( "Server started:", udp:getsockname() )
repeat
server.Parse( udp:receivefrom() )
server.Advance()
if server.tick % 50 == 0 then
server.Advertise()
end
until socket.sleep( 0.1 )
end end
function server.Advance() function server.Advance()
@ -104,4 +101,14 @@ function server.Quit()
end end
return server.Start() server.Start()
function love.update( dt )
server.Parse( udp:receivefrom() )
server.Advance()
if server.tick % 50 == 0 then
server.Advertise()
end
end
function love.draw() end

View File

@ -1 +1,3 @@
return assert(assert( require 'socket.http' ).request 'https://api.ipify.org' ) local dns = assert( require 'socket' ).dns
--Get your own local IP address via DNS.
return dns.toip( dns.gethostname() )

View File

@ -1,8 +1,10 @@
local _print = print local _print = print
local log = assert( io.open( ("../logs/log_%04d.txt"):format( os.time() % 1000 ), "a" )) 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( ... ) return function( ... )
log:write( os.date("!%S") ) log:write( os.date("!%M%S") )
for i, v in ipairs{...} do for i, v in ipairs{...} do
log:write("\t") log:write("\t")
log:write(tostring(v)) log:write(tostring(v))

View File

@ -1,7 +1,13 @@
package.cpath = ".\\?.dll;..\\build\\clib\\?.dll;"..package.cpath print( "Loading Shared." )
package.path = ".\\?.lua;..\\build\\lualib\\?.lua;"..package.path
local shared = {} local shared = {}
do
local rq = require
require = function( ... )
print( "Require:", ... )
return rq( ... )
end
end
shared.ip = assert( require 'shared.ipstring' ) shared.ip = assert( require 'shared.ipstring' )
shared.packet = assert( require 'shared.packet' ) shared.packet = assert( require 'shared.packet' )
shared.print = assert( require 'shared.print' ) shared.print = assert( require 'shared.print' )