mudgangster

Log | Files | Refs

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 }