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