chat.lua (4782B)
1 local lpeg = require( "lpeg" ) 2 3 local handleChat 4 local chatName = "FromDaHood" 5 6 local CommandBytes = { 7 nameChange = "\1", 8 all = "\4", 9 pm = "\5", 10 message = "\7", 11 version = "\19", 12 } 13 14 local Chats = { } 15 16 local parser 17 do 18 local command = lpeg.P( 1 ) 19 local terminator = lpeg.P( "\255" ) 20 local contents = ( 1 - terminator ) ^ 0 21 parser = lpeg.C( command ) * lpeg.C( contents ) * terminator 22 end 23 24 local function chatFromName( name ) 25 local idx = tonumber( name ) 26 27 if idx then 28 return Chats[ idx ] 29 end 30 31 for _, chat in ipairs( Chats ) do 32 if chat.name:startsWith( name ) then 33 return chat 34 end 35 end 36 37 return nil 38 end 39 40 local function killChat( chat ) 41 if chat.state == "killed" then 42 return 43 end 44 45 mud.print( "\n#s> Disconnected from %s!", chat.name ) 46 47 chat.state = "killed" 48 socket.close( chat.socket ) 49 50 for i, other in ipairs( Chats ) do 51 if other == chat then 52 table.remove( Chats, i ) 53 break 54 end 55 end 56 end 57 58 local function dataCoro( chat ) 59 local data = "" 60 61 while true do 62 data = data .. coroutine.yield() 63 64 if chat.state == "connecting" then 65 local name, len = data:match( "^YES:(.-)\n()" ) 66 if name then 67 chat.name = name 68 chat.state = "connected" 69 70 mud.print( "\n#s> Connected to %s@%s:%s", chat.name, chat.address, chat.port ) 71 72 data = data:sub( len ) 73 end 74 end 75 76 if chat.state == "connected" then 77 while true do 78 local command, args = parser:match( data ) 79 if not command then 80 break 81 end 82 83 data = data:sub( args:len() + 3 ) 84 85 if command == CommandBytes.all or command == CommandBytes.pm or command == CommandBytes.message then 86 local message = args:match( "^\n*(.-)\n*$" ) 87 88 handleChat( message:gsub( "\r", "" ) ) 89 end 90 end 91 end 92 end 93 end 94 95 local function dataHandler( chat, loop, watcher ) 96 local _, err, data = chat.socket:receive( "*a" ) 97 98 if err == "closed" then 99 killChat( chat ) 100 watcher:stop( loop ) 101 102 return 103 end 104 105 assert( coroutine.resume( chat.handler, data ) ) 106 end 107 108 function mud.chat_no_space( form, ... ) 109 local named = chatName .. form:format( ... ) 110 local data = CommandBytes.all .. named:parseColours() .. "\n\255" 111 112 for _, chat in ipairs( Chats ) do 113 if chat.state == "connected" then 114 socket.send( chat.socket, data ) 115 end 116 end 117 118 mud.printb( "\n#lr%s", named ) 119 handleChat() 120 end 121 122 function mud.chat( form, ... ) 123 mud.chat_no_space( " " .. form, ... ) 124 end 125 126 local function call( address, port ) 127 mud.print( "\n#s> Calling %s:%d...", address, port ) 128 129 local chat 130 local sock, err = socket.connect( address, port, function( sock, data ) 131 if data then 132 assert( coroutine.resume( chat.handler, data ) ) 133 else 134 killChat( chat ) 135 end 136 end ) 137 138 if not sock then 139 mud.print( "\n#s> Connection failed: %s", err ) 140 return 141 end 142 143 chat = { 144 name = address .. ":" .. port, 145 socket = sock, 146 147 address = address, 148 port = port, 149 150 state = "connecting", 151 handler = coroutine.create( dataCoro ), 152 153 called_at = os.time(), 154 } 155 156 assert( coroutine.resume( chat.handler, chat ) ) 157 158 table.insert( Chats, chat ) 159 160 socket.send( sock, "CHAT:%s\n127.0.0.14050 " % chatName ) 161 socket.send( sock, CommandBytes.version .. "MudGangster\255" ) 162 end 163 164 mud.alias( "/call", { 165 [ "^(%S+)$" ] = function( address ) 166 call( address, 4050 ) 167 end, 168 169 [ "^(%S+)[%s:]+(%d+)$" ] = call, 170 }, "<address> [port]" ) 171 172 mud.alias( "/hang", { 173 [ "^(%S+)$" ] = function( name ) 174 local chat = chatFromName( name ) 175 if chat then 176 killChat( chat ) 177 end 178 end, 179 } ) 180 181 local function sendPM( chat, message ) 182 local named = chatName .. " chats to you, '" .. message .. "'" 183 local data = CommandBytes.pm .. named .. "\n\255" 184 185 socket.send( chat.socket, data ) 186 end 187 188 mud.alias( "/silentpm", { 189 [ "^(%S+)%s+(.-)$" ] = function( name, message ) 190 local chat = chatFromName( name ) 191 192 if chat then 193 local coloured = message:parseColours() 194 sendPM( chat, coloured ) 195 end 196 end, 197 } ) 198 199 mud.alias( "/pm", { 200 [ "^(%S+)%s+(.-)$" ] = function( name, message ) 201 local chat = chatFromName( name ) 202 203 if chat then 204 local coloured = message:parseColours() 205 sendPM( chat, coloured ) 206 mud.printb( "\n#lrYou chat to %s, \"%s\"", chat.name, coloured ) 207 handleChat() 208 end 209 end, 210 } ) 211 212 mud.alias( "/chatname", function( name ) 213 chatName = name 214 215 local message = CommandBytes.nameChange .. chatName .. "\255" 216 for _, chat in ipairs( Chats ) do 217 if chat.state == "connected" then 218 socket.send( chat.socket, message ) 219 end 220 end 221 222 mud.print( "\n#s> Chat name changed to %s", chatName ) 223 handleChat() 224 end ) 225 226 mud.interval( function() 227 local now = os.time() 228 for i = #Chats, 1, -1 do 229 if Chats[ i ].state == "connecting" and now - Chats[ i ].called_at >= 10 then 230 mud.print( "\n#s> No response from %s", Chats[ i ].name ) 231 killChat( Chats[ i ] ) 232 end 233 end 234 end, 5 ) 235 236 return { 237 init = function( chatHandler ) 238 handleChat = chatHandler 239 end, 240 }