commit 9b1dfb15505796d9b041b9c15bdd4d9b27b57955
parent 90ba5e85aa2d02c4bdd20307546d61c2fbf61b04
Author: Michael Savage <mikejsavage@gmail.com>
Date: Mon, 3 Sep 2018 19:00:02 +0300
Pack Lua scripts into binary
Diffstat:
Makefile | | | 10 | +++++++--- |
action.lua | | | 89 | ------------------------------------------------------------------------------- |
alias.lua | | | 107 | ------------------------------------------------------------------------------- |
chat.lua | | | 228 | ------------------------------------------------------------------------------- |
connect.lua | | | 113 | ------------------------------------------------------------------------------- |
event.lua | | | 39 | --------------------------------------- |
gag.lua | | | 59 | ----------------------------------------------------------- |
handlers.lua | | | 297 | ------------------------------------------------------------------------------- |
intercept.lua | | | 43 | ------------------------------------------- |
interval.lua | | | 79 | ------------------------------------------------------------------------------- |
macro.lua | | | 31 | ------------------------------- |
main.lua | | | 69 | --------------------------------------------------------------------- |
script.lua | | | 143 | ------------------------------------------------------------------------------- |
scripts/merge.lua | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
scripts/pack_lua.sh | | | 6 | ++++++ |
serialize.lua | | | 56 | -------------------------------------------------------- |
src/lua/action.lua | | | 89 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/alias.lua | | | 107 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/chat.lua | | | 228 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/connect.lua | | | 113 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/event.lua | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
src/lua/gag.lua | | | 59 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/handlers.lua | | | 299 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/intercept.lua | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
src/lua/interval.lua | | | 79 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/macro.lua | | | 31 | +++++++++++++++++++++++++++++++ |
src/lua/main.lua | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/script.lua | | | 143 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/serialize.lua | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/status.lua | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/sub.lua | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
src/lua/utils.lua | | | 224 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/script.cc | | | 6 | +++++- |
status.lua | | | 67 | ------------------------------------------------------------------- |
sub.lua | | | 44 | -------------------------------------------- |
utils.lua | | | 224 | ------------------------------------------------------------------------------- |
36 files changed, 1754 insertions(+), 1692 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,15 +1,19 @@
all: debug
.PHONY: debug asan release clean
-debug:
+build/lua_bytecode.h: src/lua/action.lua src/lua/alias.lua src/lua/chat.lua src/lua/connect.lua src/lua/event.lua src/lua/gag.lua src/lua/handlers.lua src/lua/intercept.lua src/lua/interval.lua src/lua/macro.lua src/lua/main.lua src/lua/script.lua src/lua/serialize.lua src/lua/status.lua src/lua/sub.lua src/lua/utils.lua
+ @printf "\033[1;33mbuilding $@\033[0m\n"
+ @scripts/pack_lua.sh
+
+debug: build/lua_bytecode.h
@lua make.lua > gen.mk
@$(MAKE) -f gen.mk
-asan:
+asan: build/lua_bytecode.h
@lua make.lua asan > gen.mk
@$(MAKE) -f gen.mk
-release:
+release: build/lua_bytecode.h
@lua make.lua release > gen.mk
@$(MAKE) -f gen.mk
diff --git a/action.lua b/action.lua
@@ -1,89 +0,0 @@
-local Actions = { }
-local PreActions = { }
-local AnsiActions = { }
-local AnsiPreActions = { }
-
-local ChatActions = { }
-local ChatPreActions = { }
-local ChatAnsiActions = { }
-local ChatAnsiPreActions = { }
-
-local doActions
-local doPreActions
-local doAnsiActions
-local doAnsiPreActions
-
-local doChatActions
-local doChatPreActions
-local doChatAnsiActions
-local doChatAnsiPreActions
-
-local function genericActions( actions )
- return
- function( pattern, callback, disabled )
- enforce( pattern, "pattern", "string" )
- enforce( callback, "callback", "function", "string" )
-
- if type( callback ) == "string" then
- local command = callback
-
- callback = function()
- mud.input( command, true )
- end
- end
-
- local action = {
- pattern = pattern,
- callback = callback,
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-
- table.insert( actions, action )
-
- return action
- end,
-
- function( line )
- for i = 1, #actions do
- local action = actions[ i ]
-
- if action.enabled then
- local ok, err = pcall( string.gsub, line, action.pattern, action.callback )
-
- if not ok then
- mud.print( debug.traceback( "\n#s> action callback failed: %s" % err ) )
- end
- end
- end
- end
-end
-
-mud.action, doActions = genericActions( Actions )
-mud.preAction, doPreActions = genericActions( PreActions )
-mud.ansiAction, doAnsiActions = genericActions( AnsiActions )
-mud.ansiPreAction, doAnsiPreActions = genericActions( AnsiPreActions )
-
-mud.chatAction, doChatActions = genericActions( ChatActions )
-mud.preChatAction, doChatPreActions = genericActions( ChatPreActions )
-mud.ansiChatAction, doChatAnsiActions = genericActions( ChatAnsiActions )
-mud.ansiPreChatAction, doChatAnsiPreActions = genericActions( ChatAnsiPreActions )
-
-return {
- doActions = doActions,
- doPreActions = doPreActions,
- doAnsiActions = doAnsiActions,
- doAnsiPreActions = doAnsiPreActions,
-
- doChatActions = doChatActions,
- doChatPreActions = doChatPreActions,
- doChatAnsiActions = doChatAnsiActions,
- doChatAnsiPreActions = doChatAnsiPreActions,
-}
diff --git a/alias.lua b/alias.lua
@@ -1,107 +0,0 @@
-local Aliases = { }
-
-local function doAlias( line )
- local command, args = line:match( "^%s*(%S+)%s*(.*)$" )
- local alias = Aliases[ command ]
-
- if alias and alias.enabled then
- local badSyntax = true
-
- for i = 1, #alias.callbacks do
- local callback = alias.callbacks[ i ]
- local ok, err, subs = pcall( string.gsub, args, callback.pattern, callback.callback )
-
- if not ok then
- mud.print( debug.traceback( "\n#s> alias callback failed: %s" % err ) )
-
- return true
- end
-
- if subs ~= 0 then
- badSyntax = false
-
- break
- end
- end
-
- if badSyntax then
- mud.print( "\nsyntax: %s %s" % { command, alias.syntax } )
- end
-
- return true
- end
-
- return false
-end
-
-local function simpleAlias( callback, disabled )
- return {
- callbacks = {
- {
- pattern = "^(.*)$",
- callback = callback,
- },
- },
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-end
-
-local function patternAlias( callbacks, syntax, disabled )
- local alias = {
- callbacks = { },
- syntax = syntax,
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-
- for pattern, callback in pairs( callbacks ) do
- table.insert( alias.callbacks, {
- pattern = pattern,
- callback = callback,
- } )
- end
-
- return alias
-end
-
-function mud.alias( command, handler, ... )
- enforce( command, "command", "string" )
- enforce( handler, "handler", "function", "string", "table" )
-
- assert( not Aliases[ command ], "alias `%s' already registered" % command )
-
- if type( handler ) == "string" then
- local command = handler
-
- handler = function( args )
- mud.input( command % args )
- end
- end
-
- local alias = type( handler ) == "function"
- and simpleAlias( handler, ... )
- or patternAlias( handler, ... )
-
- Aliases[ command ] = alias
-
- return alias
-end
-
-return {
- doAlias = doAlias,
-}
diff --git a/chat.lua b/chat.lua
@@ -1,228 +0,0 @@
-local loop = ev.Loop.default
-
-local handleChat
-
-local CommandBytes = {
- all = "\4",
- pm = "\5",
- message = "\7",
- version = "\19",
-}
-
-local Clients = { }
-
-local function clientFromName( name )
- local idx = tonumber( name )
-
- if idx then
- return Clients[ idx ]
- end
-
- for _, client in ipairs( Clients ) do
- if client.name:startsWith( name ) then
- return client
- end
- end
-
- return nil
-end
-
-local function dataCoro( client )
- local data = coroutine.yield()
- local name = data:match( "^YES:(.+)\n$" )
-
- if not name then
- client.socket:shutdown()
-
- return
- end
-
- client.name = name
- client.state = "connected"
-
- mud.print( "\n#s> Connected to %s@%s:%s", client.name, client.address, client.port )
-
- local dataBuffer = ""
-
- while true do
- dataBuffer = dataBuffer .. coroutine.yield()
-
- dataBuffer = dataBuffer:gsub( "(.)(.-)\255", function( command, args )
- if command == CommandBytes.all or command == CommandBytes.pm or command == CommandBytes.message then
- local message = args:match( "^\n*(.-)\n*$" )
-
- handleChat( message:gsub( "\r", "" ) )
- end
-
- return ""
- end )
- end
-end
-
-local Client = { }
-
-function Client:new( socket, address, port )
- socket:setoption( "keepalive", true )
-
- local client = {
- socket = socket,
-
- address = address,
- port = port,
-
- state = "connecting",
- handler = coroutine.create( dataCoro ),
- }
-
- assert( coroutine.resume( client.handler, client ) )
-
- setmetatable( client, { __index = Client } )
-
- table.insert( Clients, client )
-
- socket:send( "CHAT:Hirve\n127.0.0.14050 " )
-
- return client
-end
-
-function Client:kill()
- if self.state == "killed" then
- return
- end
-
- mud.print( "\n#s> Disconnected from %s!", self.name )
-
- self.state = "killed"
- self.socket:shutdown()
-
- for i, client in ipairs( Clients ) do
- if client == self then
- table.remove( Clients, i )
- break
- end
- end
-end
-
-local function dataHandler( client, loop, watcher )
- local _, err, data = client.socket:receive( "*a" )
-
- if err == "closed" then
- client:kill()
- watcher:stop( loop )
-
- return
- end
-
- assert( coroutine.resume( client.handler, data ) )
- mud.handleXEvents()
-end
-
-function mud.chatns( form, ... )
- local named = "\nHirve" .. form:format( ... )
- local data = CommandBytes.all .. named:parseColours() .. "\n\255"
-
- for _, client in ipairs( Clients ) do
- if client.state == "connected" then
- client.socket:send( data )
- end
- end
-
- mud.printb( "#lr%s", named )
- handleChat()
-end
-
-function mud.chat( form, ... )
- mud.chatns( " " .. form, ... )
-end
-
-local function call( address, port )
- mud.print( "\n#s> Calling %s:%d...", address, port )
-
- local sock = socket.tcp()
- sock:settimeout( 0 )
-
- sock:connect( address, port )
-
- ev.IO.new( function( loop, watcher )
- local _, err = sock:receive( "*a" )
-
- if err ~= "connection refused" then
- local client = Client:new( sock, address, port )
-
- ev.IO.new( function( loop, watcher )
- dataHandler( client, loop, watcher )
- end, sock:getfd(), ev.READ ):start( loop )
-
- ev.IO.new( function( loop, watcher )
- client.socket:send( CommandBytes.version .. "MudGangster" .. "\255" )
- watcher:stop( loop )
- end, sock:getfd(), ev.WRITE ):start( loop )
- else
- mud.print( "\n#s> Failed to call %s:%d", address, port )
- end
-
- watcher:stop( loop )
- end, sock:getfd(), ev.WRITE ):start( loop )
-end
-
-mud.alias( "/call", {
- [ "^(%S+)$" ] = function( address )
- call( address, 4050 )
- end,
-
- [ "^(%S+)[%s:]+(%d+)$" ] = call,
-}, "<address> [port]" )
-
-mud.alias( "/hang", {
- [ "^(%S+)$" ] = function( name )
- local client = clientFromName( name )
-
- if client then
- client:kill()
- end
- end,
-} )
-
-local function sendPM( client, message )
- local named = "\nHirve chats to you, '" .. message .. "'"
- local data = CommandBytes.pm .. named .. "\n\255"
-
- client.socket:send( data )
-end
-
-mud.alias( "/silentpm", {
- [ "^(%S+)%s+(.-)$" ] = function( name, message )
- local client = clientFromName( name )
-
- if client then
- local coloured = message:parseColours()
-
- sendPM( client, coloured )
- end
- end,
-} )
-
-mud.alias( "/pm", {
- [ "^(%S+)%s+(.-)$" ] = function( name, message )
- local client = clientFromName( name )
-
- if client then
- local coloured = message:parseColours()
-
- sendPM( client, coloured )
-
- local toPrint = ( "You chat to %s, '" % client.name ) .. coloured .. "'"
-
- handleChat( toPrint )
- end
- end,
-} )
-
-mud.alias( "/emoteto", function( args )
-end )
-
-return {
- init = function( chatHandler )
- handleChat = chatHandler
- end,
-}
diff --git a/connect.lua b/connect.lua
@@ -1,113 +0,0 @@
-local DataHandler
-
-local LastAddress
-local LastPort
-
-local loop = ev.Loop.default
-
-function mud.disconnect()
- if not mud.connected then
- mud.print( "\n#s> You're not connected..." )
-
- return
- end
-
- mud.connected = false
-
- mud.kill()
-end
-
-function mud.connect( address, port )
- if mud.connected then
- mud.print( "\n#s> Already connected! (%s:%d)", LastAddress, LastPort )
-
- return
- end
-
- if not address then
- if not LastAddress then
- mud.print( "\n#s> I need an address..." )
-
- return
- end
-
- address = LastAddress
- port = LastPort
- end
-
- local sock = socket.tcp()
- sock:settimeout( 0 )
- sock:connect( address, port )
-
- mud.print( "\n#s> Connecting to %s:%d...", address, port )
-
- mud.connected = true
- mud.lastInput = mud.now()
-
- mud.send = function( data )
- mud.lastInput = mud.now()
-
- sock:send( data )
- end
-
- mud.kill = function()
- sock:shutdown()
- end
-
- ev.IO.new( function( loop, watcher )
- local _, err = sock:receive( "*a" )
-
- if err ~= "connection refused" then
- LastAddress = address
- LastPort = port
-
- ev.IO.new( function( loop, watcher )
- local _, err, data = sock:receive( "*a" )
-
- if not data then
- data = _
- end
-
- if data then
- DataHandler( data )
- mud.handleXEvents()
- end
-
- if err == "closed" then
- mud.print( "\n#s> Disconnected!" )
-
- watcher:stop( loop )
- sock:shutdown()
-
- mud.connected = false
-
- return
- end
- end, sock:getfd(), ev.READ ):start( loop )
- else
- mud.print( "\n#s> Refused!" )
-
- mud.connected = false
- end
-
- watcher:stop( loop )
- end, sock:getfd(), ev.WRITE ):start( loop )
-end
-
-mud.alias( "/con", {
- [ "^$" ] = function()
- mud.connect()
- end,
-
- [ "^(%S+)%s+(%d+)$" ] = function( address, port )
- mud.connect( address, port )
- end,
-}, "<ip> <port>" )
-
-mud.alias( "/dc", mud.disconnect )
-
-return {
- init = function( dataHandler )
- DataHandler = dataHandler
- end,
-}
diff --git a/event.lua b/event.lua
@@ -1,39 +0,0 @@
-local Events = { }
-
-function mud.listen( name, callback, disabled )
- enforce( name, "name", "string" )
- enforce( callback, "callback", "function" )
-
- local event = {
- callback = callback,
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-
- if not Events[ name ] then
- Events[ name ] = { }
- end
-
- table.insert( Events[ name ], event )
-
- return event
-end
-
-function mud.event( name, ... )
- enforce( name, "name", "string" )
-
- if Events[ name ] then
- for _, event in ipairs( Events[ name ] ) do
- if event.enabled then
- event.callback( ... )
- end
- end
- end
-end
diff --git a/gag.lua b/gag.lua
@@ -1,59 +0,0 @@
-local Gags = { }
-local AnsiGags = { }
-
-local ChatGags = { }
-local AnsiChatGags = { }
-
-local doGags
-local doAnsiGags
-
-local doChatGags
-local doAnsiChatGags
-
-local function genericGags( gags )
- return
- function( pattern, disabled )
- local gag = {
- pattern = pattern,
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-
- table.insert( gags, gag )
-
- return gag
- end,
-
- function( line )
- for i = 1, #gags do
- local gag = gags[ i ]
-
- if gag.enabled and line:find( gag.pattern ) then
- return true
- end
- end
-
- return false
- end
-end
-
-mud.gag, doGags = genericGags( Gags )
-mud.gagAnsi, doAnsiGags = genericGags( AnsiGags )
-
-mud.gagChat, doChatGags = genericGags( ChatGags )
-mud.gagAnsiChat, doChatAnsiGags = genericGags( ChatAnsiGags )
-
-return {
- doGags = doGags,
- doAnsiGags = doAnsiGags,
-
- doChatGags = doChatGags,
- doChatAnsiGags = doChatAnsiGags,
-}
diff --git a/handlers.lua b/handlers.lua
@@ -1,297 +0,0 @@
-local action = require( "action" )
-local alias = require( "alias" )
-local intercept = require( "intercept" )
-local gag = require( "gag" )
-local macro = require( "macro" )
-local sub = require( "sub" )
-local interval = require( "interval" )
-
-local GA = "\255\249"
-
-local bold = false
-local fg = 7
-local bg = 0
-
-local lastWasChat = false
-local lastWasGA = false
-
-local receiving = false
-local showInput = true
-
-local dataBuffer = ""
-local pendingInputs = { }
-
-local function echoOn()
- showInput = true
-
- return ""
-end
-
-local function echoOff()
- showInput = true
-
- return ""
-end
-
-local function setFG( colour )
- return function()
- fg = colour
- end
-end
-
-local function setBG( colour )
- return function()
- bg = colour
- end
-end
-
-local Escapes = {
- m = {
- [ "0" ] = function()
- bold = false
- fg = 7
- bg = 0
- end,
-
- [ "1" ] = function()
- bold = true
- end,
-
- [ "30" ] = setFG( 0 ),
- [ "31" ] = setFG( 1 ),
- [ "32" ] = setFG( 2 ),
- [ "33" ] = setFG( 3 ),
- [ "34" ] = setFG( 4 ),
- [ "35" ] = setFG( 5 ),
- [ "36" ] = setFG( 6 ),
- [ "37" ] = setFG( 7 ),
-
- [ "40" ] = setBG( 0 ),
- [ "41" ] = setBG( 1 ),
- [ "42" ] = setBG( 2 ),
- [ "43" ] = setBG( 3 ),
- [ "44" ] = setBG( 4 ),
- [ "45" ] = setBG( 5 ),
- [ "46" ] = setBG( 6 ),
- [ "47" ] = setBG( 7 ),
- },
-}
-
-local function printPendingInputs()
- if lastWasChat then
- mud.newlineMain()
-
- lastWasChat = false
- end
-
- for i = 1, #pendingInputs do
- mud.printr( pendingInputs[ i ] )
-
- lastWasGA = false
- end
-
- pendingInputs = { }
-end
-
-local function handleChat( message )
- lastWasChat = true
- lastWasGA = false
-
- if not message then
- return
- end
-
- local oldFG = fg
- local oldBG = bg
- local oldBold = bold
-
- fg = 1
- bg = 0
- bold = true
-
- local noAnsi = message:gsub( "\27%[[%d;]*%a", "" )
-
- action.doChatPreActions( noAnsi )
- action.doChatAnsiPreActions( message )
-
- mud.newlineMain()
- mud.newlineChat()
-
- for line, newLine in message:gmatch( "([^\n]*)(\n?)" ) do
- for text, opts, escape in ( line .. "\27[m" ):gmatch( "(.-)\27%[([%d;]*)(%a)" ) do
- if text ~= "" then
- mud.printMain( text, fg, bg, bold )
- mud.printChat( text, fg, bg, bold )
- end
-
- for opt in opts:gmatch( "([^;]+)" ) do
- if Escapes[ escape ][ opt ] then
- Escapes[ escape ][ opt ]()
- end
- end
- end
-
- if newLine == "\n" then
- mud.newlineMain()
- mud.newlineChat()
- end
- end
-
- mud.drawMain()
- mud.drawChat()
-
- action.doChatActions( noAnsi )
- action.doChatAnsiActions( message )
-
- fg = oldFG
- bg = oldBG
- bold = oldBold
-end
-
-local function handleData( data )
- receiving = true
-
- dataBuffer = dataBuffer .. data
-
- if data:match( GA ) then
- dataBuffer = dataBuffer:gsub( "\r", "" )
-
- dataBuffer = dataBuffer:gsub( "\255\252\1", echoOn )
- dataBuffer = dataBuffer:gsub( "\255\251\1", echoOff )
-
- dataBuffer = dataBuffer:gsub( "\255\253\24", "" )
- dataBuffer = dataBuffer:gsub( "\255\253\31", "" )
- dataBuffer = dataBuffer:gsub( "\255\253\34", "" )
-
- dataBuffer = dataBuffer .. "\n"
-
- for line in dataBuffer:gmatch( "([^\n]*)\n" ) do
- if lastWasGA then
- if line ~= "" then
- mud.newlineMain()
- end
-
- lastWasGA = false
- end
-
- if lastWasChat then
- mud.newlineMain()
-
- lastWasChat = false
- end
-
- local clean, subs = line:gsub( GA, "" )
- local hasGA = subs ~= 0
-
- local noAnsi = clean:gsub( "\27%[%d*%a", "" )
-
- local gagged = gag.doGags( noAnsi ) or gag.doAnsiGags( clean )
-
- action.doPreActions( noAnsi )
- action.doAnsiPreActions( clean )
-
- local subbed = sub.doSubs( clean )
-
- for text, opts, escape in ( subbed .. "\27[m" ):gmatch( "(.-)\27%[([%d;]*)(%a)" ) do
- if text ~= "" and not gagged then
- mud.printMain( text, fg, bg, bold )
- end
-
- for opt in opts:gmatch( "([^;]+)" ) do
- if Escapes[ escape ][ opt ] then
- Escapes[ escape ][ opt ]()
- end
- end
- end
-
- if hasGA then
- lastWasGA = true
- printPendingInputs()
- else
- if not gagged then
- mud.newlineMain()
- end
- end
-
- action.doActions( noAnsi )
- action.doAnsiActions( clean )
- end
-
- mud.drawMain()
-
- dataBuffer = ""
- receiving = false
- end
-end
-
-local function handleCommand( input, hide )
- if not alias.doAlias( input ) then
- if not mud.connected then
- mud.print( "\n#s> You're not connected..." )
-
- return
- end
-
- intercept.doIntercept( input )
-
- if not hide and input ~= "" then
- local toShow = ( showInput and input or ( "*" ):rep( input:len() ) ) .. "\n"
-
- if receiving then
- table.insert( pendingInputs, toShow )
- else
- if lastWasChat then
- mud.newlineMain()
-
- lastWasChat = false
- end
-
- lastWasGA = false
-
- mud.printr( toShow )
- end
- end
-
- mud.send( input .. "\n" )
- mud.drawMain()
- end
-end
-
-local function handleInput( input, display )
- for command in ( input .. ";" ):gmatch( "([^;]*);" ) do
- handleCommand( command, display )
- end
-end
-
-mud.input = handleInput
-
-local function handleMacro( key, shift, ctrl, alt )
- if shift then
- key = "s" .. key
- end
-
- if ctrl then
- key = "c" .. key
- end
-
- if alt then
- key = "a" .. key
- end
-
- macro.doMacro( key )
-end
-
-local function handleClose()
- ev.Loop.default:unloop()
-
- mud.event( "shutdown" )
-end
-
-return {
- data = handleData,
- chat = handleChat,
- input = handleInput,
- macro = handleMacro,
- interval = interval.doIntervals,
- close = handleClose,
-}
diff --git a/intercept.lua b/intercept.lua
@@ -1,43 +0,0 @@
-local Intercepts = { }
-
-local function doIntercept( command )
- for i = 1, #Intercepts do
- local intercept = Intercepts[ i ]
-
- if intercept.enabled then
- local ok, err = pcall( string.gsub, command, intercept.pattern, intercept.callback )
-
- if not ok then
- mud.print( debug.traceback( "\n#s> intercept callback failed: %s" % err ) )
- end
- end
- end
-end
-
-function mud.intercept( pattern, callback, disabled )
- enforce( pattern, "pattern", "string" )
- enforce( callback, "callback", "function" )
- enforce( disabled, "disabled", "boolean", "nil" )
-
- local intercept = {
- pattern = pattern,
- callback = callback,
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-
- table.insert( Intercepts, intercept )
-
- return intercept
-end
-
-return {
- doIntercept = doIntercept,
-}
diff --git a/interval.lua b/interval.lua
@@ -1,79 +0,0 @@
-local Intervals = { }
-
-local function doIntervals( loop, watcher )
- local now = loop:update_now()
-
- for i = 1, #Intervals do
- local event = Intervals[ i ]
-
- if event.enabled then
- event:checkTick( now )
- end
- end
-end
-
-function mud.now()
- return ev.Loop.default:update_now()
-end
-
-function mud.interval( callback, interval, disabled )
- enforce( callback, "callback", "function" )
- enforce( interval, "interval", "number" )
-
- local event = {
- callback = callback,
- interval = interval,
- nextTick = mud.now(),
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
-
- tick = function( self, now )
- callback( now )
-
- self.nextTick = now + self.interval
- end,
-
- checkTick = function( self, now )
- if self.nextTick == -1 then
- self.nextTick = now
- end
-
- if now >= self.nextTick then
- self:tick( now )
- end
- end,
-
- tend = function( self, tolerance, desired )
- tolerance = tolerance or 0
- desired = desired or mud.now()
-
- local toTick = ( self.nextTick - desired ) % self.interval
- local sinceTick = self.interval - toTick
-
- if toTick <= tolerance then
- self.nextTick = math.avg( self.nextTick, desired )
- elseif sinceTick <= tolerance then
- self.nextTick = math.avg( self.nextTick, desired + self.interval )
- else
- self.nextTick = desired
-
- self:checkTick( mud.now() )
- end
- end,
- }
-
- table.insert( Intervals, event )
-
- return event
-end
-
-return {
- doIntervals = doIntervals,
-}
diff --git a/macro.lua b/macro.lua
@@ -1,31 +0,0 @@
-local Macros = { }
-
-local function doMacro( key )
- local macro = Macros[ key ]
-
- if macro then
- macro()
- end
-end
-
-function mud.macro( key, callback, disabled )
- enforce( key, "key", "string" )
- enforce( callback, "callback", "function", "string" )
-
- if type( callback ) == "string" then
- local command = callback
-
- callback = function()
- mud.input( command )
- end
- end
-
- assert( not Macros[ key ], "macro `%s' already registered" % key )
-
-
- Macros[ key ] = callback
-end
-
-return {
- doMacro = doMacro,
-}
diff --git a/main.lua b/main.lua
@@ -1,69 +0,0 @@
-mud = {
- connected = false,
-}
-
--- local function luarocks_path( opt, orig )
--- local pipe = io.popen( "luarocks path " .. opt, "r" )
--- if not pipe then
--- return nil
--- end
---
--- local contents = pipe:read( "*all" )
--- pipe:close()
---
--- if not contents then
--- return orig
--- end
---
--- return contents:gsub( "%s*$", "" ) .. ";" .. orig
--- end
---
--- package.path = luarocks_path( "--lr-path", package.path )
--- package.cpath = luarocks_path( "--lr-cpath", package.cpath )
-
-table.unpack = table.unpack or unpack
-
-require( "utils" )
-
-socket = require( "socket" )
-ev = require( "ev" )
-
-require( "event" )
-
-local script = require( "script" )
-local handlers = require( "handlers" )
-
-require( "chat" ).init( handlers.chat )
-require( "connect" ).init( handlers.data )
-
-local xFD, handleXEvents,
- printMain, newlineMain, drawMain,
- printChat, newlineChat, drawChat,
- setHandlers, urgent, setStatus = ...
-
-mud.printMain = printMain
-mud.newlineMain = newlineMain
-mud.drawMain = drawMain
-
-mud.printChat = printChat
-mud.newlineChat = newlineChat
-mud.drawChat = drawChat
-
-mud.urgent = urgent
-
-require( "status" ).init( setStatus )
-
-setHandlers( handlers.input, handlers.macro, handlers.close )
-
-local loop = ev.Loop.default
-
-mud.handleXEvents = handleXEvents
-
-ev.IO.new( handleXEvents, xFD, ev.READ ):start( loop )
-ev.Timer.new( handlers.interval, 0.5, 0.5 ):start( loop )
-
-script.load()
-
-loop:loop()
-
-script.close()
diff --git a/script.lua b/script.lua
@@ -1,143 +0,0 @@
-local lfs = require( "lfs" )
-local serialize = require( "serialize" )
-
-local ScriptsDir = os.getenv( "HOME" ) .. "/.mudgangster/scripts"
-
-package.path = package.path .. ";" .. ScriptsDir .. "/?.lua"
-
-local function loadScript( name, path, padding )
- local function throw( err )
- if err:match( "^" .. path ) then
- err = err:sub( path:len() + 2 )
- end
-
- mud.print( "#lr%-" .. padding .. "s", name )
- mud.print( "#s\n>\n> %s\n>", err )
- end
-
- mud.print( "#s\n> " )
-
- local mainPath = path .. "/main.lua"
- local readable, err = io.readable( mainPath )
-
- if not readable then
- throw( err )
-
- return
- end
-
- local env = setmetatable(
- {
- require = function( requirePath, ... )
- return require( "%s.%s" % { name, requirePath }, ... )
- end,
-
- saved = function( defaults )
- local settingsPath = path .. "/settings.lua"
-
- mud.listen( "shutdown", function()
- local file = io.open( settingsPath, "w" )
-
- file:write( serialize.unwrapped( defaults ) )
-
- file:close()
- end )
-
- local settingsEnv = setmetatable( { }, {
- __newindex = defaults,
- } )
-
- local settings = loadfile( settingsPath, "t", settingsEnv )
-
- if not settings then
- return defaults
- end
-
- if _VERSION == "Lua 5.1" then
- setfenv( settings, settingsEnv )
- end
-
- pcall( settings )
-
- return defaults
- end,
- },
- {
- __index = _G,
- __newindex = _G,
- }
- )
-
- local script, err = loadfile( mainPath, "t", env )
-
- if not script then
- throw( err )
-
- return
- end
-
- if _VERSION == "Lua 5.1" then
- setfenv( script, env )
- end
-
- local loaded, err = pcall( script )
-
- if loaded then
- mud.print( "#lg%s", name )
- else
- throw( err )
- end
-end
-
-local function loadScripts()
- mud.print( "#s> Loading scripts..." )
-
- local readable, err = io.readable( ScriptsDir )
-
- if readable then
- local attr = lfs.attributes( ScriptsDir )
-
- if attr.mode == "directory" then
- local scripts = { }
- local maxLen = 0
-
- for script in lfs.dir( ScriptsDir ) do
- if not script:match( "^%." ) then
- local path = ScriptsDir .. "/" .. script
- local scriptAttr = lfs.attributes( path )
-
- if scriptAttr.mode == "directory" then
- maxLen = math.max( script:len(), maxLen )
-
- table.insert( scripts, {
- name = script,
- path = path,
- } )
- end
- end
- end
-
- table.sort( scripts, function( a, b )
- return a.name < b.name
- end )
-
- for _, script in ipairs( scripts ) do
- loadScript( script.name, script.path, maxLen )
- end
- else
- mud.print( "#sfailed!\n> `%s' isn't a directory", ScriptsDir )
- end
- else
- mud.print( "#sfailed!\n> %s", err )
- end
-
- mud.event( "scriptsLoaded" )
-end
-
-local function closeScripts()
-end
-
-return {
- load = loadScripts,
- close = closeScripts,
-}
diff --git a/scripts/merge.lua b/scripts/merge.lua
@@ -0,0 +1,46 @@
+if not arg[ 1 ] or not arg[ 2 ] then
+ print( arg[ 0 ] .. " <source directory> <path to main> [Lua version]" )
+ os.exit( 1 )
+end
+
+local lfs = require( "lfs" )
+
+local merged = { "#! /usr/bin/env lua" .. ( arg[ 3 ] or "" ) }
+
+local root = arg[ 1 ]
+local main = arg[ 2 ]
+
+local function addDir( rel )
+ for file in lfs.dir( root .. "/" .. rel ) do
+ if file ~= "." and file ~= ".." then
+ local full = root .. "/" .. rel .. file
+ local attr = lfs.attributes( full )
+
+ if attr.mode == "directory" then
+ addDir( rel .. file .. "/" )
+ elseif file:match( "%.lua$" ) and ( rel ~= "" or file ~= main ) then
+ local f = io.open( full, "r" )
+ local contents = f:read( "*all" )
+ f:close()
+
+ table.insert( merged, ( [[
+ package.preload[ "%s" ] = function( ... )
+ %s
+ end]] ):format(
+ ( rel .. file ):gsub( "%.lua$", "" ):gsub( "/", "." ),
+ contents
+ )
+ )
+ end
+ end
+ end
+end
+
+addDir( "" )
+
+local f = io.open( root .. "/" .. main, "r" )
+local contents = f:read( "*all" )
+f:close()
+
+print( table.concat( merged, "\n" ) )
+print( contents )
diff --git a/scripts/pack_lua.sh b/scripts/pack_lua.sh
@@ -0,0 +1,6 @@
+#! /bin/sh
+
+set -o pipefail
+
+mkdir -p build
+exec lua scripts/merge.lua src/lua main.lua | luac -o - - | xxd -i > build/lua_bytecode.h
diff --git a/serialize.lua b/serialize.lua
@@ -1,56 +0,0 @@
--- can't handle cycles, only works on strings/numbers/bools/tables
-
-local function formatKey( key )
- if type( key ) == "string" then
- return "[ %q ]" % key
- end
-
- return "[ %s ]" % tostring( key )
-end
-
-local function serializeObject( obj )
- local t = type( obj )
-
- if t == "number" or t == "boolean" then
- return tostring( obj )
- end
-
- if t == "string" then
- return "%q" % obj
- end
-
- if t == "table" then
- local output = "{ "
-
- for k, v in pairs( obj ) do
- output = output .. "%s = %s, " % { formatKey( k ), serializeObject( v ) }
- end
-
- return output .. "}"
- end
-
- error( "I don't know how to serialize type " .. t )
-end
-
-local function serialize( obj )
- if not obj then
- return "return { }"
- end
-
- return "return " .. serializeObject( obj )
-end
-
-local function serializeUnwrapped( obj )
- local output = ""
-
- for k, v in pairs( obj ) do
- output = output .. "%s = %s\n" % { k, serializeObject( v ) }
- end
-
- return output
-end
-
-return {
- wrapped = serialize,
- unwrapped = serializeUnwrapped,
-}
diff --git a/src/lua/action.lua b/src/lua/action.lua
@@ -0,0 +1,89 @@
+local Actions = { }
+local PreActions = { }
+local AnsiActions = { }
+local AnsiPreActions = { }
+
+local ChatActions = { }
+local ChatPreActions = { }
+local ChatAnsiActions = { }
+local ChatAnsiPreActions = { }
+
+local doActions
+local doPreActions
+local doAnsiActions
+local doAnsiPreActions
+
+local doChatActions
+local doChatPreActions
+local doChatAnsiActions
+local doChatAnsiPreActions
+
+local function genericActions( actions )
+ return
+ function( pattern, callback, disabled )
+ enforce( pattern, "pattern", "string" )
+ enforce( callback, "callback", "function", "string" )
+
+ if type( callback ) == "string" then
+ local command = callback
+
+ callback = function()
+ mud.input( command, true )
+ end
+ end
+
+ local action = {
+ pattern = pattern,
+ callback = callback,
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+
+ table.insert( actions, action )
+
+ return action
+ end,
+
+ function( line )
+ for i = 1, #actions do
+ local action = actions[ i ]
+
+ if action.enabled then
+ local ok, err = pcall( string.gsub, line, action.pattern, action.callback )
+
+ if not ok then
+ mud.print( debug.traceback( "\n#s> action callback failed: %s" % err ) )
+ end
+ end
+ end
+ end
+end
+
+mud.action, doActions = genericActions( Actions )
+mud.preAction, doPreActions = genericActions( PreActions )
+mud.ansiAction, doAnsiActions = genericActions( AnsiActions )
+mud.ansiPreAction, doAnsiPreActions = genericActions( AnsiPreActions )
+
+mud.chatAction, doChatActions = genericActions( ChatActions )
+mud.preChatAction, doChatPreActions = genericActions( ChatPreActions )
+mud.ansiChatAction, doChatAnsiActions = genericActions( ChatAnsiActions )
+mud.ansiPreChatAction, doChatAnsiPreActions = genericActions( ChatAnsiPreActions )
+
+return {
+ doActions = doActions,
+ doPreActions = doPreActions,
+ doAnsiActions = doAnsiActions,
+ doAnsiPreActions = doAnsiPreActions,
+
+ doChatActions = doChatActions,
+ doChatPreActions = doChatPreActions,
+ doChatAnsiActions = doChatAnsiActions,
+ doChatAnsiPreActions = doChatAnsiPreActions,
+}
diff --git a/src/lua/alias.lua b/src/lua/alias.lua
@@ -0,0 +1,107 @@
+local Aliases = { }
+
+local function doAlias( line )
+ local command, args = line:match( "^%s*(%S+)%s*(.*)$" )
+ local alias = Aliases[ command ]
+
+ if alias and alias.enabled then
+ local badSyntax = true
+
+ for i = 1, #alias.callbacks do
+ local callback = alias.callbacks[ i ]
+ local ok, err, subs = pcall( string.gsub, args, callback.pattern, callback.callback )
+
+ if not ok then
+ mud.print( debug.traceback( "\n#s> alias callback failed: %s" % err ) )
+
+ return true
+ end
+
+ if subs ~= 0 then
+ badSyntax = false
+
+ break
+ end
+ end
+
+ if badSyntax then
+ mud.print( "\nsyntax: %s %s" % { command, alias.syntax } )
+ end
+
+ return true
+ end
+
+ return false
+end
+
+local function simpleAlias( callback, disabled )
+ return {
+ callbacks = {
+ {
+ pattern = "^(.*)$",
+ callback = callback,
+ },
+ },
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+end
+
+local function patternAlias( callbacks, syntax, disabled )
+ local alias = {
+ callbacks = { },
+ syntax = syntax,
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+
+ for pattern, callback in pairs( callbacks ) do
+ table.insert( alias.callbacks, {
+ pattern = pattern,
+ callback = callback,
+ } )
+ end
+
+ return alias
+end
+
+function mud.alias( command, handler, ... )
+ enforce( command, "command", "string" )
+ enforce( handler, "handler", "function", "string", "table" )
+
+ assert( not Aliases[ command ], "alias `%s' already registered" % command )
+
+ if type( handler ) == "string" then
+ local command = handler
+
+ handler = function( args )
+ mud.input( command % args )
+ end
+ end
+
+ local alias = type( handler ) == "function"
+ and simpleAlias( handler, ... )
+ or patternAlias( handler, ... )
+
+ Aliases[ command ] = alias
+
+ return alias
+end
+
+return {
+ doAlias = doAlias,
+}
diff --git a/src/lua/chat.lua b/src/lua/chat.lua
@@ -0,0 +1,228 @@
+local loop = ev.Loop.default
+
+local handleChat
+
+local CommandBytes = {
+ all = "\4",
+ pm = "\5",
+ message = "\7",
+ version = "\19",
+}
+
+local Clients = { }
+
+local function clientFromName( name )
+ local idx = tonumber( name )
+
+ if idx then
+ return Clients[ idx ]
+ end
+
+ for _, client in ipairs( Clients ) do
+ if client.name:startsWith( name ) then
+ return client
+ end
+ end
+
+ return nil
+end
+
+local function dataCoro( client )
+ local data = coroutine.yield()
+ local name = data:match( "^YES:(.+)\n$" )
+
+ if not name then
+ client.socket:shutdown()
+
+ return
+ end
+
+ client.name = name
+ client.state = "connected"
+
+ mud.print( "\n#s> Connected to %s@%s:%s", client.name, client.address, client.port )
+
+ local dataBuffer = ""
+
+ while true do
+ dataBuffer = dataBuffer .. coroutine.yield()
+
+ dataBuffer = dataBuffer:gsub( "(.)(.-)\255", function( command, args )
+ if command == CommandBytes.all or command == CommandBytes.pm or command == CommandBytes.message then
+ local message = args:match( "^\n*(.-)\n*$" )
+
+ handleChat( message:gsub( "\r", "" ) )
+ end
+
+ return ""
+ end )
+ end
+end
+
+local Client = { }
+
+function Client:new( socket, address, port )
+ socket:setoption( "keepalive", true )
+
+ local client = {
+ socket = socket,
+
+ address = address,
+ port = port,
+
+ state = "connecting",
+ handler = coroutine.create( dataCoro ),
+ }
+
+ assert( coroutine.resume( client.handler, client ) )
+
+ setmetatable( client, { __index = Client } )
+
+ table.insert( Clients, client )
+
+ socket:send( "CHAT:Hirve\n127.0.0.14050 " )
+
+ return client
+end
+
+function Client:kill()
+ if self.state == "killed" then
+ return
+ end
+
+ mud.print( "\n#s> Disconnected from %s!", self.name )
+
+ self.state = "killed"
+ self.socket:shutdown()
+
+ for i, client in ipairs( Clients ) do
+ if client == self then
+ table.remove( Clients, i )
+ break
+ end
+ end
+end
+
+local function dataHandler( client, loop, watcher )
+ local _, err, data = client.socket:receive( "*a" )
+
+ if err == "closed" then
+ client:kill()
+ watcher:stop( loop )
+
+ return
+ end
+
+ assert( coroutine.resume( client.handler, data ) )
+ mud.handleXEvents()
+end
+
+function mud.chatns( form, ... )
+ local named = "\nHirve" .. form:format( ... )
+ local data = CommandBytes.all .. named:parseColours() .. "\n\255"
+
+ for _, client in ipairs( Clients ) do
+ if client.state == "connected" then
+ client.socket:send( data )
+ end
+ end
+
+ mud.printb( "#lr%s", named )
+ handleChat()
+end
+
+function mud.chat( form, ... )
+ mud.chatns( " " .. form, ... )
+end
+
+local function call( address, port )
+ mud.print( "\n#s> Calling %s:%d...", address, port )
+
+ local sock = socket.tcp()
+ sock:settimeout( 0 )
+
+ sock:connect( address, port )
+
+ ev.IO.new( function( loop, watcher )
+ local _, err = sock:receive( "*a" )
+
+ if err ~= "connection refused" then
+ local client = Client:new( sock, address, port )
+
+ ev.IO.new( function( loop, watcher )
+ dataHandler( client, loop, watcher )
+ end, sock:getfd(), ev.READ ):start( loop )
+
+ ev.IO.new( function( loop, watcher )
+ client.socket:send( CommandBytes.version .. "MudGangster" .. "\255" )
+ watcher:stop( loop )
+ end, sock:getfd(), ev.WRITE ):start( loop )
+ else
+ mud.print( "\n#s> Failed to call %s:%d", address, port )
+ end
+
+ watcher:stop( loop )
+ end, sock:getfd(), ev.WRITE ):start( loop )
+end
+
+mud.alias( "/call", {
+ [ "^(%S+)$" ] = function( address )
+ call( address, 4050 )
+ end,
+
+ [ "^(%S+)[%s:]+(%d+)$" ] = call,
+}, "<address> [port]" )
+
+mud.alias( "/hang", {
+ [ "^(%S+)$" ] = function( name )
+ local client = clientFromName( name )
+
+ if client then
+ client:kill()
+ end
+ end,
+} )
+
+local function sendPM( client, message )
+ local named = "\nHirve chats to you, '" .. message .. "'"
+ local data = CommandBytes.pm .. named .. "\n\255"
+
+ client.socket:send( data )
+end
+
+mud.alias( "/silentpm", {
+ [ "^(%S+)%s+(.-)$" ] = function( name, message )
+ local client = clientFromName( name )
+
+ if client then
+ local coloured = message:parseColours()
+
+ sendPM( client, coloured )
+ end
+ end,
+} )
+
+mud.alias( "/pm", {
+ [ "^(%S+)%s+(.-)$" ] = function( name, message )
+ local client = clientFromName( name )
+
+ if client then
+ local coloured = message:parseColours()
+
+ sendPM( client, coloured )
+
+ local toPrint = ( "You chat to %s, '" % client.name ) .. coloured .. "'"
+
+ handleChat( toPrint )
+ end
+ end,
+} )
+
+mud.alias( "/emoteto", function( args )
+end )
+
+return {
+ init = function( chatHandler )
+ handleChat = chatHandler
+ end,
+}
diff --git a/src/lua/connect.lua b/src/lua/connect.lua
@@ -0,0 +1,113 @@
+local DataHandler
+
+local LastAddress
+local LastPort
+
+local loop = ev.Loop.default
+
+function mud.disconnect()
+ if not mud.connected then
+ mud.print( "\n#s> You're not connected..." )
+
+ return
+ end
+
+ mud.connected = false
+
+ mud.kill()
+end
+
+function mud.connect( address, port )
+ if mud.connected then
+ mud.print( "\n#s> Already connected! (%s:%d)", LastAddress, LastPort )
+
+ return
+ end
+
+ if not address then
+ if not LastAddress then
+ mud.print( "\n#s> I need an address..." )
+
+ return
+ end
+
+ address = LastAddress
+ port = LastPort
+ end
+
+ local sock = socket.tcp()
+ sock:settimeout( 0 )
+ sock:connect( address, port )
+
+ mud.print( "\n#s> Connecting to %s:%d...", address, port )
+
+ mud.connected = true
+ mud.lastInput = mud.now()
+
+ mud.send = function( data )
+ mud.lastInput = mud.now()
+
+ sock:send( data )
+ end
+
+ mud.kill = function()
+ sock:shutdown()
+ end
+
+ ev.IO.new( function( loop, watcher )
+ local _, err = sock:receive( "*a" )
+
+ if err ~= "connection refused" then
+ LastAddress = address
+ LastPort = port
+
+ ev.IO.new( function( loop, watcher )
+ local _, err, data = sock:receive( "*a" )
+
+ if not data then
+ data = _
+ end
+
+ if data then
+ DataHandler( data )
+ mud.handleXEvents()
+ end
+
+ if err == "closed" then
+ mud.print( "\n#s> Disconnected!" )
+
+ watcher:stop( loop )
+ sock:shutdown()
+
+ mud.connected = false
+
+ return
+ end
+ end, sock:getfd(), ev.READ ):start( loop )
+ else
+ mud.print( "\n#s> Refused!" )
+
+ mud.connected = false
+ end
+
+ watcher:stop( loop )
+ end, sock:getfd(), ev.WRITE ):start( loop )
+end
+
+mud.alias( "/con", {
+ [ "^$" ] = function()
+ mud.connect()
+ end,
+
+ [ "^(%S+)%s+(%d+)$" ] = function( address, port )
+ mud.connect( address, port )
+ end,
+}, "<ip> <port>" )
+
+mud.alias( "/dc", mud.disconnect )
+
+return {
+ init = function( dataHandler )
+ DataHandler = dataHandler
+ end,
+}
diff --git a/src/lua/event.lua b/src/lua/event.lua
@@ -0,0 +1,39 @@
+local Events = { }
+
+function mud.listen( name, callback, disabled )
+ enforce( name, "name", "string" )
+ enforce( callback, "callback", "function" )
+
+ local event = {
+ callback = callback,
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+
+ if not Events[ name ] then
+ Events[ name ] = { }
+ end
+
+ table.insert( Events[ name ], event )
+
+ return event
+end
+
+function mud.event( name, ... )
+ enforce( name, "name", "string" )
+
+ if Events[ name ] then
+ for _, event in ipairs( Events[ name ] ) do
+ if event.enabled then
+ event.callback( ... )
+ end
+ end
+ end
+end
diff --git a/src/lua/gag.lua b/src/lua/gag.lua
@@ -0,0 +1,59 @@
+local Gags = { }
+local AnsiGags = { }
+
+local ChatGags = { }
+local AnsiChatGags = { }
+
+local doGags
+local doAnsiGags
+
+local doChatGags
+local doAnsiChatGags
+
+local function genericGags( gags )
+ return
+ function( pattern, disabled )
+ local gag = {
+ pattern = pattern,
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+
+ table.insert( gags, gag )
+
+ return gag
+ end,
+
+ function( line )
+ for i = 1, #gags do
+ local gag = gags[ i ]
+
+ if gag.enabled and line:find( gag.pattern ) then
+ return true
+ end
+ end
+
+ return false
+ end
+end
+
+mud.gag, doGags = genericGags( Gags )
+mud.gagAnsi, doAnsiGags = genericGags( AnsiGags )
+
+mud.gagChat, doChatGags = genericGags( ChatGags )
+mud.gagAnsiChat, doChatAnsiGags = genericGags( ChatAnsiGags )
+
+return {
+ doGags = doGags,
+ doAnsiGags = doAnsiGags,
+
+ doChatGags = doChatGags,
+ doChatAnsiGags = doChatAnsiGags,
+}
diff --git a/src/lua/handlers.lua b/src/lua/handlers.lua
@@ -0,0 +1,299 @@
+local action = require( "action" )
+local alias = require( "alias" )
+local intercept = require( "intercept" )
+local gag = require( "gag" )
+local macro = require( "macro" )
+local sub = require( "sub" )
+local interval = require( "interval" )
+
+local lpeg = require( "lpeg" )
+
+local GA = "\255\249"
+
+local bold = false
+local fg = 7
+local bg = 0
+
+local lastWasChat = false
+local lastWasGA = false
+
+local receiving = false
+local showInput = true
+
+local dataBuffer = ""
+local pendingInputs = { }
+
+local function echoOn()
+ showInput = true
+
+ return ""
+end
+
+local function echoOff()
+ showInput = true
+
+ return ""
+end
+
+local function setFG( colour )
+ return function()
+ fg = colour
+ end
+end
+
+local function setBG( colour )
+ return function()
+ bg = colour
+ end
+end
+
+local Escapes = {
+ m = {
+ [ "0" ] = function()
+ bold = false
+ fg = 7
+ bg = 0
+ end,
+
+ [ "1" ] = function()
+ bold = true
+ end,
+
+ [ "30" ] = setFG( 0 ),
+ [ "31" ] = setFG( 1 ),
+ [ "32" ] = setFG( 2 ),
+ [ "33" ] = setFG( 3 ),
+ [ "34" ] = setFG( 4 ),
+ [ "35" ] = setFG( 5 ),
+ [ "36" ] = setFG( 6 ),
+ [ "37" ] = setFG( 7 ),
+
+ [ "40" ] = setBG( 0 ),
+ [ "41" ] = setBG( 1 ),
+ [ "42" ] = setBG( 2 ),
+ [ "43" ] = setBG( 3 ),
+ [ "44" ] = setBG( 4 ),
+ [ "45" ] = setBG( 5 ),
+ [ "46" ] = setBG( 6 ),
+ [ "47" ] = setBG( 7 ),
+ },
+}
+
+local function printPendingInputs()
+ if lastWasChat then
+ mud.newlineMain()
+
+ lastWasChat = false
+ end
+
+ for i = 1, #pendingInputs do
+ mud.printr( pendingInputs[ i ] )
+
+ lastWasGA = false
+ end
+
+ pendingInputs = { }
+end
+
+local function handleChat( message )
+ lastWasChat = true
+ lastWasGA = false
+
+ if not message then
+ return
+ end
+
+ local oldFG = fg
+ local oldBG = bg
+ local oldBold = bold
+
+ fg = 1
+ bg = 0
+ bold = true
+
+ local noAnsi = message:gsub( "\27%[[%d;]*%a", "" )
+
+ action.doChatPreActions( noAnsi )
+ action.doChatAnsiPreActions( message )
+
+ mud.newlineMain()
+ mud.newlineChat()
+
+ for line, newLine in message:gmatch( "([^\n]*)(\n?)" ) do
+ for text, opts, escape in ( line .. "\27[m" ):gmatch( "(.-)\27%[([%d;]*)(%a)" ) do
+ if text ~= "" then
+ mud.printMain( text, fg, bg, bold )
+ mud.printChat( text, fg, bg, bold )
+ end
+
+ for opt in opts:gmatch( "([^;]+)" ) do
+ if Escapes[ escape ][ opt ] then
+ Escapes[ escape ][ opt ]()
+ end
+ end
+ end
+
+ if newLine == "\n" then
+ mud.newlineMain()
+ mud.newlineChat()
+ end
+ end
+
+ mud.drawMain()
+ mud.drawChat()
+
+ action.doChatActions( noAnsi )
+ action.doChatAnsiActions( message )
+
+ fg = oldFG
+ bg = oldBG
+ bold = oldBold
+end
+
+local function handleData( data )
+ receiving = true
+
+ dataBuffer = dataBuffer .. data
+
+ if data:match( GA ) then
+ dataBuffer = dataBuffer:gsub( "\r", "" )
+
+ dataBuffer = dataBuffer:gsub( "\255\252\1", echoOn )
+ dataBuffer = dataBuffer:gsub( "\255\251\1", echoOff )
+
+ dataBuffer = dataBuffer:gsub( "\255\253\24", "" )
+ dataBuffer = dataBuffer:gsub( "\255\253\31", "" )
+ dataBuffer = dataBuffer:gsub( "\255\253\34", "" )
+
+ dataBuffer = dataBuffer .. "\n"
+
+ for line in dataBuffer:gmatch( "([^\n]*)\n" ) do
+ if lastWasGA then
+ if line ~= "" then
+ mud.newlineMain()
+ end
+
+ lastWasGA = false
+ end
+
+ if lastWasChat then
+ mud.newlineMain()
+
+ lastWasChat = false
+ end
+
+ local clean, subs = line:gsub( GA, "" )
+ local hasGA = subs ~= 0
+
+ local noAnsi = clean:gsub( "\27%[%d*%a", "" )
+
+ local gagged = gag.doGags( noAnsi ) or gag.doAnsiGags( clean )
+
+ action.doPreActions( noAnsi )
+ action.doAnsiPreActions( clean )
+
+ local subbed = sub.doSubs( clean )
+
+ for text, opts, escape in ( subbed .. "\27[m" ):gmatch( "(.-)\27%[([%d;]*)(%a)" ) do
+ if text ~= "" and not gagged then
+ mud.printMain( text, fg, bg, bold )
+ end
+
+ for opt in opts:gmatch( "([^;]+)" ) do
+ if Escapes[ escape ][ opt ] then
+ Escapes[ escape ][ opt ]()
+ end
+ end
+ end
+
+ if hasGA then
+ lastWasGA = true
+ printPendingInputs()
+ else
+ if not gagged then
+ mud.newlineMain()
+ end
+ end
+
+ action.doActions( noAnsi )
+ action.doAnsiActions( clean )
+ end
+
+ mud.drawMain()
+
+ dataBuffer = ""
+ receiving = false
+ end
+end
+
+local function handleCommand( input, hide )
+ if not alias.doAlias( input ) then
+ if not mud.connected then
+ mud.print( "\n#s> You're not connected..." )
+
+ return
+ end
+
+ intercept.doIntercept( input )
+
+ if not hide and input ~= "" then
+ local toShow = ( showInput and input or ( "*" ):rep( input:len() ) ) .. "\n"
+
+ if receiving then
+ table.insert( pendingInputs, toShow )
+ else
+ if lastWasChat then
+ mud.newlineMain()
+
+ lastWasChat = false
+ end
+
+ lastWasGA = false
+
+ mud.printr( toShow )
+ end
+ end
+
+ mud.send( input .. "\n" )
+ mud.drawMain()
+ end
+end
+
+local function handleInput( input, display )
+ for command in ( input .. ";" ):gmatch( "([^;]*);" ) do
+ handleCommand( command, display )
+ end
+end
+
+mud.input = handleInput
+
+local function handleMacro( key, shift, ctrl, alt )
+ if shift then
+ key = "s" .. key
+ end
+
+ if ctrl then
+ key = "c" .. key
+ end
+
+ if alt then
+ key = "a" .. key
+ end
+
+ macro.doMacro( key )
+end
+
+local function handleClose()
+ ev.Loop.default:unloop()
+
+ mud.event( "shutdown" )
+end
+
+return {
+ data = handleData,
+ chat = handleChat,
+ input = handleInput,
+ macro = handleMacro,
+ interval = interval.doIntervals,
+ close = handleClose,
+}
diff --git a/src/lua/intercept.lua b/src/lua/intercept.lua
@@ -0,0 +1,43 @@
+local Intercepts = { }
+
+local function doIntercept( command )
+ for i = 1, #Intercepts do
+ local intercept = Intercepts[ i ]
+
+ if intercept.enabled then
+ local ok, err = pcall( string.gsub, command, intercept.pattern, intercept.callback )
+
+ if not ok then
+ mud.print( debug.traceback( "\n#s> intercept callback failed: %s" % err ) )
+ end
+ end
+ end
+end
+
+function mud.intercept( pattern, callback, disabled )
+ enforce( pattern, "pattern", "string" )
+ enforce( callback, "callback", "function" )
+ enforce( disabled, "disabled", "boolean", "nil" )
+
+ local intercept = {
+ pattern = pattern,
+ callback = callback,
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+
+ table.insert( Intercepts, intercept )
+
+ return intercept
+end
+
+return {
+ doIntercept = doIntercept,
+}
diff --git a/src/lua/interval.lua b/src/lua/interval.lua
@@ -0,0 +1,79 @@
+local Intervals = { }
+
+local function doIntervals( loop, watcher )
+ local now = loop:update_now()
+
+ for i = 1, #Intervals do
+ local event = Intervals[ i ]
+
+ if event.enabled then
+ event:checkTick( now )
+ end
+ end
+end
+
+function mud.now()
+ return ev.Loop.default:update_now()
+end
+
+function mud.interval( callback, interval, disabled )
+ enforce( callback, "callback", "function" )
+ enforce( interval, "interval", "number" )
+
+ local event = {
+ callback = callback,
+ interval = interval,
+ nextTick = mud.now(),
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+
+ tick = function( self, now )
+ callback( now )
+
+ self.nextTick = now + self.interval
+ end,
+
+ checkTick = function( self, now )
+ if self.nextTick == -1 then
+ self.nextTick = now
+ end
+
+ if now >= self.nextTick then
+ self:tick( now )
+ end
+ end,
+
+ tend = function( self, tolerance, desired )
+ tolerance = tolerance or 0
+ desired = desired or mud.now()
+
+ local toTick = ( self.nextTick - desired ) % self.interval
+ local sinceTick = self.interval - toTick
+
+ if toTick <= tolerance then
+ self.nextTick = math.avg( self.nextTick, desired )
+ elseif sinceTick <= tolerance then
+ self.nextTick = math.avg( self.nextTick, desired + self.interval )
+ else
+ self.nextTick = desired
+
+ self:checkTick( mud.now() )
+ end
+ end,
+ }
+
+ table.insert( Intervals, event )
+
+ return event
+end
+
+return {
+ doIntervals = doIntervals,
+}
diff --git a/src/lua/macro.lua b/src/lua/macro.lua
@@ -0,0 +1,31 @@
+local Macros = { }
+
+local function doMacro( key )
+ local macro = Macros[ key ]
+
+ if macro then
+ macro()
+ end
+end
+
+function mud.macro( key, callback, disabled )
+ enforce( key, "key", "string" )
+ enforce( callback, "callback", "function", "string" )
+
+ if type( callback ) == "string" then
+ local command = callback
+
+ callback = function()
+ mud.input( command )
+ end
+ end
+
+ assert( not Macros[ key ], "macro `%s' already registered" % key )
+
+
+ Macros[ key ] = callback
+end
+
+return {
+ doMacro = doMacro,
+}
diff --git a/src/lua/main.lua b/src/lua/main.lua
@@ -0,0 +1,69 @@
+mud = {
+ connected = false,
+}
+
+-- local function luarocks_path( opt, orig )
+-- local pipe = io.popen( "luarocks path " .. opt, "r" )
+-- if not pipe then
+-- return nil
+-- end
+--
+-- local contents = pipe:read( "*all" )
+-- pipe:close()
+--
+-- if not contents then
+-- return orig
+-- end
+--
+-- return contents:gsub( "%s*$", "" ) .. ";" .. orig
+-- end
+--
+-- package.path = luarocks_path( "--lr-path", package.path )
+-- package.cpath = luarocks_path( "--lr-cpath", package.cpath )
+
+table.unpack = table.unpack or unpack
+
+require( "utils" )
+
+socket = require( "socket" )
+ev = require( "ev" )
+
+require( "event" )
+
+local script = require( "script" )
+local handlers = require( "handlers" )
+
+require( "chat" ).init( handlers.chat )
+require( "connect" ).init( handlers.data )
+
+local xFD, handleXEvents,
+ printMain, newlineMain, drawMain,
+ printChat, newlineChat, drawChat,
+ setHandlers, urgent, setStatus = ...
+
+mud.printMain = printMain
+mud.newlineMain = newlineMain
+mud.drawMain = drawMain
+
+mud.printChat = printChat
+mud.newlineChat = newlineChat
+mud.drawChat = drawChat
+
+mud.urgent = urgent
+
+require( "status" ).init( setStatus )
+
+setHandlers( handlers.input, handlers.macro, handlers.close )
+
+local loop = ev.Loop.default
+
+mud.handleXEvents = handleXEvents
+
+ev.IO.new( handleXEvents, xFD, ev.READ ):start( loop )
+ev.Timer.new( handlers.interval, 0.5, 0.5 ):start( loop )
+
+script.load()
+
+loop:loop()
+
+script.close()
diff --git a/src/lua/script.lua b/src/lua/script.lua
@@ -0,0 +1,143 @@
+local lfs = require( "lfs" )
+local serialize = require( "serialize" )
+
+local ScriptsDir = os.getenv( "HOME" ) .. "/.mudgangster/scripts"
+
+package.path = package.path .. ";" .. ScriptsDir .. "/?.lua"
+
+local function loadScript( name, path, padding )
+ local function throw( err )
+ if err:match( "^" .. path ) then
+ err = err:sub( path:len() + 2 )
+ end
+
+ mud.print( "#lr%-" .. padding .. "s", name )
+ mud.print( "#s\n>\n> %s\n>", err )
+ end
+
+ mud.print( "#s\n> " )
+
+ local mainPath = path .. "/main.lua"
+ local readable, err = io.readable( mainPath )
+
+ if not readable then
+ throw( err )
+
+ return
+ end
+
+ local env = setmetatable(
+ {
+ require = function( requirePath, ... )
+ return require( "%s.%s" % { name, requirePath }, ... )
+ end,
+
+ saved = function( defaults )
+ local settingsPath = path .. "/settings.lua"
+
+ mud.listen( "shutdown", function()
+ local file = io.open( settingsPath, "w" )
+
+ file:write( serialize.unwrapped( defaults ) )
+
+ file:close()
+ end )
+
+ local settingsEnv = setmetatable( { }, {
+ __newindex = defaults,
+ } )
+
+ local settings = loadfile( settingsPath, "t", settingsEnv )
+
+ if not settings then
+ return defaults
+ end
+
+ if _VERSION == "Lua 5.1" then
+ setfenv( settings, settingsEnv )
+ end
+
+ pcall( settings )
+
+ return defaults
+ end,
+ },
+ {
+ __index = _G,
+ __newindex = _G,
+ }
+ )
+
+ local script, err = loadfile( mainPath, "t", env )
+
+ if not script then
+ throw( err )
+
+ return
+ end
+
+ if _VERSION == "Lua 5.1" then
+ setfenv( script, env )
+ end
+
+ local loaded, err = pcall( script )
+
+ if loaded then
+ mud.print( "#lg%s", name )
+ else
+ throw( err )
+ end
+end
+
+local function loadScripts()
+ mud.print( "#s> Loading scripts..." )
+
+ local readable, err = io.readable( ScriptsDir )
+
+ if readable then
+ local attr = lfs.attributes( ScriptsDir )
+
+ if attr.mode == "directory" then
+ local scripts = { }
+ local maxLen = 0
+
+ for script in lfs.dir( ScriptsDir ) do
+ if not script:match( "^%." ) then
+ local path = ScriptsDir .. "/" .. script
+ local scriptAttr = lfs.attributes( path )
+
+ if scriptAttr.mode == "directory" then
+ maxLen = math.max( script:len(), maxLen )
+
+ table.insert( scripts, {
+ name = script,
+ path = path,
+ } )
+ end
+ end
+ end
+
+ table.sort( scripts, function( a, b )
+ return a.name < b.name
+ end )
+
+ for _, script in ipairs( scripts ) do
+ loadScript( script.name, script.path, maxLen )
+ end
+ else
+ mud.print( "#sfailed!\n> `%s' isn't a directory", ScriptsDir )
+ end
+ else
+ mud.print( "#sfailed!\n> %s", err )
+ end
+
+ mud.event( "scriptsLoaded" )
+end
+
+local function closeScripts()
+end
+
+return {
+ load = loadScripts,
+ close = closeScripts,
+}
diff --git a/src/lua/serialize.lua b/src/lua/serialize.lua
@@ -0,0 +1,56 @@
+-- can't handle cycles, only works on strings/numbers/bools/tables
+
+local function formatKey( key )
+ if type( key ) == "string" then
+ return "[ %q ]" % key
+ end
+
+ return "[ %s ]" % tostring( key )
+end
+
+local function serializeObject( obj )
+ local t = type( obj )
+
+ if t == "number" or t == "boolean" then
+ return tostring( obj )
+ end
+
+ if t == "string" then
+ return "%q" % obj
+ end
+
+ if t == "table" then
+ local output = "{ "
+
+ for k, v in pairs( obj ) do
+ output = output .. "%s = %s, " % { formatKey( k ), serializeObject( v ) }
+ end
+
+ return output .. "}"
+ end
+
+ error( "I don't know how to serialize type " .. t )
+end
+
+local function serialize( obj )
+ if not obj then
+ return "return { }"
+ end
+
+ return "return " .. serializeObject( obj )
+end
+
+local function serializeUnwrapped( obj )
+ local output = ""
+
+ for k, v in pairs( obj ) do
+ output = output .. "%s = %s\n" % { k, serializeObject( v ) }
+ end
+
+ return output
+end
+
+return {
+ wrapped = serialize,
+ unwrapped = serializeUnwrapped,
+}
diff --git a/src/lua/status.lua b/src/lua/status.lua
@@ -0,0 +1,67 @@
+local setStatus
+local barItems = { }
+
+local function drawBar()
+ local status = { }
+
+ for _, item in ipairs( barItems ) do
+ local fg = 7
+ local bold = false
+
+ for text, opts, escape in ( item.text .. "\27[m" ):gmatch( "(.-)\27%[([%d;]*)(%a)" ) do
+ if text ~= "" then
+ table.insert( status, {
+ text = text,
+ fg = fg,
+ bold = bold,
+ } )
+ end
+
+ for opt in opts:gmatch( "([^;]+)" ) do
+ if opt == "0" then
+ fg = 7
+ bold = false
+ elseif opt == "1" then
+ bold = true
+ else
+ opt = tonumber( opt )
+
+ if opt and opt >= 30 and opt <= 37 then
+ fg = opt - 30
+ end
+ end
+ end
+ end
+ end
+
+ setStatus( status )
+
+ mud.handleXEvents()
+end
+
+function mud.bar( priority )
+ local item = {
+ priority = priority,
+ text = "",
+
+ set = function( self, form, ... )
+ enforce( form, "form", "string" )
+
+ self.text = string.parseColours( form:format( ... ) )
+ drawBar()
+ end,
+ }
+
+ table.insert( barItems, item )
+ table.sort( barItems, function( a, b )
+ return a.priority < b.priority
+ end )
+
+ return item
+end
+
+return {
+ init = function( set )
+ setStatus = set
+ end,
+}
diff --git a/src/lua/sub.lua b/src/lua/sub.lua
@@ -0,0 +1,44 @@
+local Subs = { }
+
+local function doSubs( line )
+ for i = 1, #Subs do
+ local sub = Subs[ i ]
+
+ if sub.enabled then
+ local newLine, subs = line:gsub( sub.pattern, sub.replacement )
+
+ if subs ~= 0 then
+ return newLine
+ end
+ end
+ end
+
+ return line
+end
+
+function mud.sub( pattern, replacement, disabled )
+ enforce( pattern, "pattern", "string" )
+ enforce( replacement, "replacement", "string", "function" )
+
+ local sub = {
+ pattern = pattern,
+ replacement = replacement,
+
+ enabled = not disabled,
+
+ enable = function( self )
+ self.enabled = true
+ end,
+ disable = function( self )
+ self.enabled = false
+ end,
+ }
+
+ table.insert( Subs, sub )
+
+ return sub
+end
+
+return {
+ doSubs = doSubs,
+}
diff --git a/src/lua/utils.lua b/src/lua/utils.lua
@@ -0,0 +1,224 @@
+getmetatable( "" ).__mod = function( self, form )
+ if type( form ) == "table" then
+ return self:format( table.unpack( form ) )
+ end
+
+ return self:format( form )
+end
+
+function string.plural( count, plur, sing )
+ return count == 1 and ( sing or "" ) or ( plur or "s" )
+end
+
+function string.startsWith( self, needle, ignoreCase )
+ if ignoreCase then
+ return self:lower():sub( 1, needle:len() ) == needle:lower()
+ end
+
+ return self:sub( 1, needle:len() ) == needle
+end
+
+function string.commas( num )
+ num = tonumber( num )
+
+ local out = ""
+
+ while num >= 1000 do
+ out = ( ",%03d%s" ):format( num % 1000, out )
+
+ num = math.floor( num / 1000 )
+ end
+
+ return tostring( num ) .. out
+end
+
+function math.round( num )
+ return math.floor( num + 0.5 )
+end
+
+function math.avg( a, b )
+ return ( a + b ) / 2
+end
+
+function io.readable( path )
+ local file, err = io.open( path, "r" )
+
+ if not file then
+ return false, err
+ end
+
+ io.close( file )
+
+ return true
+end
+
+function enforce( var, name, ... )
+ local acceptable = { ... }
+ local ok = false
+
+ for _, accept in ipairs( acceptable ) do
+ if type( var ) == accept then
+ ok = true
+
+ break
+ end
+ end
+
+ if not ok then
+ error( "argument `%s' to %s should be of type %s (got %s)" % { name, debug.getinfo( 2, "n" ).name, table.concat( acceptable, " or " ), type( var ) }, 3 )
+ end
+end
+
+local function genericPrint( message, main, chat )
+ local bold = false
+ local fg = 7
+ local bg = 0
+
+ local function setFG( colour )
+ return function()
+ fg = colour
+ end
+ end
+
+ local Sequences = {
+ d = setFG( 7 ),
+ k = setFG( 0 ),
+ r = setFG( 1 ),
+ g = setFG( 2 ),
+ y = setFG( 3 ),
+ b = setFG( 4 ),
+ m = setFG( 5 ),
+ c = setFG( 6 ),
+ w = setFG( 7 ),
+ s = setFG( 8 ),
+ }
+
+ for line, newLine in message:gmatch( "([^\n]*)(\n?)" ) do
+ for text, hashPos, light, colour, colourPos in ( line .. "#a" ):gmatch( "(.-)()#(l?)(%l)()" ) do
+ if main then
+ mud.printMain( text:gsub( "##", "#" ), fg, bg, bold )
+ end
+
+ if chat then
+ mud.printChat( text:gsub( "##", "#" ), fg, bg, bold )
+ end
+
+ if line:sub( hashPos - 1, hashPos - 1 ) ~= "#" then
+ if colourPos ~= line:len() + 3 then
+ assert( Sequences[ colour ], "invalid colour sequence: #" .. light .. colour )
+
+ bold = light == "l"
+ Sequences[ colour ]()
+ end
+ else
+ if main then
+ mud.printMain( light .. colour, fg, bg, bold )
+ end
+
+ if chat then
+ mud.printChat( light .. colour, fg, bg, bold )
+ end
+ end
+ end
+
+ if newLine ~= "" then
+ if main then
+ mud.newlineMain()
+ end
+
+ if chat then
+ mud.newlineChat()
+ end
+ end
+ end
+
+ if main then
+ mud.drawMain()
+ end
+
+ if chat then
+ mud.drawChat()
+ end
+
+ mud.handleXEvents()
+end
+
+function mud.print( form, ... )
+ genericPrint( form:format( ... ), true, false )
+end
+
+function mud.printc( form, ... )
+ genericPrint( form:format( ... ), false, true )
+end
+
+function mud.printb( form, ... )
+ genericPrint( form:format( ... ), true, true )
+end
+
+function mud.printr( str, fg, bg, bold )
+ fg = fg or 7
+ bg = bg or 0
+ bold = bold or false
+
+ for line, newLine in str:gmatch( "([^\n]*)(\n?)" ) do
+ mud.printMain( line, fg, bg, bold )
+
+ if newLine ~= "" then
+ mud.newlineMain()
+ end
+ end
+
+ mud.drawMain()
+end
+
+function mud.enable( ... )
+ local things = { ... }
+
+ if not things[ 1 ].enable then
+ things = things[ 1 ]
+ end
+
+ for _, thing in ipairs( things ) do
+ thing:enable()
+ end
+end
+
+function mud.disable( ... )
+ local things = { ... }
+
+ if not things[ 1 ].enable then
+ things = things[ 1 ]
+ end
+
+ for _, thing in ipairs( things ) do
+ thing:disable()
+ end
+end
+
+local ColourSequences = {
+ d = 0,
+ k = 30,
+ r = 31,
+ g = 32,
+ y = 33,
+ b = 34,
+ m = 35,
+ c = 36,
+ w = 37,
+}
+
+function string.parseColours( message )
+ message = assert( tostring( message ) )
+
+ return ( message:gsub( "()#(l?)(%l)", function( patternPos, bold, sequence )
+ if message:sub( patternPos - 1, patternPos - 1 ) == "#" then
+ return
+ end
+
+ if bold == "l" then
+ return "\27[1m\27[%dm" % { ColourSequences[ sequence ] }
+ end
+
+ return "\27[0m\27[%dm" % { ColourSequences[ sequence ] }
+ end ):gsub( "##", "#" ) )
+end
diff --git a/src/script.cc b/src/script.cc
@@ -13,6 +13,10 @@
#define luaL_len lua_objlen
#endif
+static const uint8_t lua_bytecode[] = {
+#include "build/lua_bytecode.h"
+};
+
static lua_State * lua;
static int inputHandlerIdx = LUA_NOREF;
@@ -176,7 +180,7 @@ void script_init() {
lua_getfield( lua, -1, "traceback" );
lua_remove( lua, -2 );
- if( luaL_loadfile( lua, "main.lua" ) ) {
+ if( luaL_loadbufferx( lua, ( const char * ) lua_bytecode, sizeof( lua_bytecode ), "main", "b" ) != LUA_OK ) {
printf( "Error reading main.lua: %s\n", lua_tostring( lua, -1 ) );
exit( 1 );
diff --git a/status.lua b/status.lua
@@ -1,67 +0,0 @@
-local setStatus
-local barItems = { }
-
-local function drawBar()
- local status = { }
-
- for _, item in ipairs( barItems ) do
- local fg = 7
- local bold = false
-
- for text, opts, escape in ( item.text .. "\27[m" ):gmatch( "(.-)\27%[([%d;]*)(%a)" ) do
- if text ~= "" then
- table.insert( status, {
- text = text,
- fg = fg,
- bold = bold,
- } )
- end
-
- for opt in opts:gmatch( "([^;]+)" ) do
- if opt == "0" then
- fg = 7
- bold = false
- elseif opt == "1" then
- bold = true
- else
- opt = tonumber( opt )
-
- if opt and opt >= 30 and opt <= 37 then
- fg = opt - 30
- end
- end
- end
- end
- end
-
- setStatus( status )
-
- mud.handleXEvents()
-end
-
-function mud.bar( priority )
- local item = {
- priority = priority,
- text = "",
-
- set = function( self, form, ... )
- enforce( form, "form", "string" )
-
- self.text = string.parseColours( form:format( ... ) )
- drawBar()
- end,
- }
-
- table.insert( barItems, item )
- table.sort( barItems, function( a, b )
- return a.priority < b.priority
- end )
-
- return item
-end
-
-return {
- init = function( set )
- setStatus = set
- end,
-}
diff --git a/sub.lua b/sub.lua
@@ -1,44 +0,0 @@
-local Subs = { }
-
-local function doSubs( line )
- for i = 1, #Subs do
- local sub = Subs[ i ]
-
- if sub.enabled then
- local newLine, subs = line:gsub( sub.pattern, sub.replacement )
-
- if subs ~= 0 then
- return newLine
- end
- end
- end
-
- return line
-end
-
-function mud.sub( pattern, replacement, disabled )
- enforce( pattern, "pattern", "string" )
- enforce( replacement, "replacement", "string", "function" )
-
- local sub = {
- pattern = pattern,
- replacement = replacement,
-
- enabled = not disabled,
-
- enable = function( self )
- self.enabled = true
- end,
- disable = function( self )
- self.enabled = false
- end,
- }
-
- table.insert( Subs, sub )
-
- return sub
-end
-
-return {
- doSubs = doSubs,
-}
diff --git a/utils.lua b/utils.lua
@@ -1,224 +0,0 @@
-getmetatable( "" ).__mod = function( self, form )
- if type( form ) == "table" then
- return self:format( table.unpack( form ) )
- end
-
- return self:format( form )
-end
-
-function string.plural( count, plur, sing )
- return count == 1 and ( sing or "" ) or ( plur or "s" )
-end
-
-function string.startsWith( self, needle, ignoreCase )
- if ignoreCase then
- return self:lower():sub( 1, needle:len() ) == needle:lower()
- end
-
- return self:sub( 1, needle:len() ) == needle
-end
-
-function string.commas( num )
- num = tonumber( num )
-
- local out = ""
-
- while num >= 1000 do
- out = ( ",%03d%s" ):format( num % 1000, out )
-
- num = math.floor( num / 1000 )
- end
-
- return tostring( num ) .. out
-end
-
-function math.round( num )
- return math.floor( num + 0.5 )
-end
-
-function math.avg( a, b )
- return ( a + b ) / 2
-end
-
-function io.readable( path )
- local file, err = io.open( path, "r" )
-
- if not file then
- return false, err
- end
-
- io.close( file )
-
- return true
-end
-
-function enforce( var, name, ... )
- local acceptable = { ... }
- local ok = false
-
- for _, accept in ipairs( acceptable ) do
- if type( var ) == accept then
- ok = true
-
- break
- end
- end
-
- if not ok then
- error( "argument `%s' to %s should be of type %s (got %s)" % { name, debug.getinfo( 2, "n" ).name, table.concat( acceptable, " or " ), type( var ) }, 3 )
- end
-end
-
-local function genericPrint( message, main, chat )
- local bold = false
- local fg = 7
- local bg = 0
-
- local function setFG( colour )
- return function()
- fg = colour
- end
- end
-
- local Sequences = {
- d = setFG( 7 ),
- k = setFG( 0 ),
- r = setFG( 1 ),
- g = setFG( 2 ),
- y = setFG( 3 ),
- b = setFG( 4 ),
- m = setFG( 5 ),
- c = setFG( 6 ),
- w = setFG( 7 ),
- s = setFG( 8 ),
- }
-
- for line, newLine in message:gmatch( "([^\n]*)(\n?)" ) do
- for text, hashPos, light, colour, colourPos in ( line .. "#a" ):gmatch( "(.-)()#(l?)(%l)()" ) do
- if main then
- mud.printMain( text:gsub( "##", "#" ), fg, bg, bold )
- end
-
- if chat then
- mud.printChat( text:gsub( "##", "#" ), fg, bg, bold )
- end
-
- if line:sub( hashPos - 1, hashPos - 1 ) ~= "#" then
- if colourPos ~= line:len() + 3 then
- assert( Sequences[ colour ], "invalid colour sequence: #" .. light .. colour )
-
- bold = light == "l"
- Sequences[ colour ]()
- end
- else
- if main then
- mud.printMain( light .. colour, fg, bg, bold )
- end
-
- if chat then
- mud.printChat( light .. colour, fg, bg, bold )
- end
- end
- end
-
- if newLine ~= "" then
- if main then
- mud.newlineMain()
- end
-
- if chat then
- mud.newlineChat()
- end
- end
- end
-
- if main then
- mud.drawMain()
- end
-
- if chat then
- mud.drawChat()
- end
-
- mud.handleXEvents()
-end
-
-function mud.print( form, ... )
- genericPrint( form:format( ... ), true, false )
-end
-
-function mud.printc( form, ... )
- genericPrint( form:format( ... ), false, true )
-end
-
-function mud.printb( form, ... )
- genericPrint( form:format( ... ), true, true )
-end
-
-function mud.printr( str, fg, bg, bold )
- fg = fg or 7
- bg = bg or 0
- bold = bold or false
-
- for line, newLine in str:gmatch( "([^\n]*)(\n?)" ) do
- mud.printMain( line, fg, bg, bold )
-
- if newLine ~= "" then
- mud.newlineMain()
- end
- end
-
- mud.drawMain()
-end
-
-function mud.enable( ... )
- local things = { ... }
-
- if not things[ 1 ].enable then
- things = things[ 1 ]
- end
-
- for _, thing in ipairs( things ) do
- thing:enable()
- end
-end
-
-function mud.disable( ... )
- local things = { ... }
-
- if not things[ 1 ].enable then
- things = things[ 1 ]
- end
-
- for _, thing in ipairs( things ) do
- thing:disable()
- end
-end
-
-local ColourSequences = {
- d = 0,
- k = 30,
- r = 31,
- g = 32,
- y = 33,
- b = 34,
- m = 35,
- c = 36,
- w = 37,
-}
-
-function string.parseColours( message )
- message = assert( tostring( message ) )
-
- return ( message:gsub( "()#(l?)(%l)", function( patternPos, bold, sequence )
- if message:sub( patternPos - 1, patternPos - 1 ) == "#" then
- return
- end
-
- if bold == "l" then
- return "\27[1m\27[%dm" % { ColourSequences[ sequence ] }
- end
-
- return "\27[0m\27[%dm" % { ColourSequences[ sequence ] }
- end ):gsub( "##", "#" ) )
-end