Advertisement
Guest User

chat

a guest
Nov 27th, 2015
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.68 KB | None | 0 0
  1. local tArgs = { ... }
  2.  
  3. local function printUsage()
  4.     print( "Usages:" )
  5.     print( "chat host <hostname>" )
  6.     print( "chat join <hostname> <nickname>" )
  7. end
  8.  
  9. local tCommands
  10. tCommands = {
  11.     ["broadcast"] = function( tUser, sContent )
  12.       if string.len(sContent) > 0 then
  13.         send( "* ".. sContent )
  14.       else
  15.         send( "* Usage: /broadcast [words]", tUser.nUserID )
  16.       end
  17.     end,
  18.     ["me"] = function( tUser, sContent )
  19.         if string.len(sContent) > 0 then
  20.             send( "* "..tUser.sUsername.." "..sContent )
  21.         else
  22.             send( "* Usage: /me [words]", tUser.nUserID )
  23.         end
  24.     end,
  25.     ["nick"] = function( tUser, sContent )
  26.         if string.len(sContent) > 0 then
  27.             local sOldName = tUser.sUsername
  28.             tUser.sUsername = sContent
  29.             send( "* "..sOldName.." is now known as "..tUser.sUsername )
  30.         else
  31.             send( "* Usage: /nick [nickname]", tUser.nUserID )
  32.         end
  33.     end,
  34.     ["users"] = function( tUser, sContent )
  35.         send( "* Connected Users:", tUser.nUserID )
  36.         local sUsers = "*"
  37.         for nUserID, tUser in pairs( tUsers ) do
  38.             sUsers = sUsers .. " " .. tUser.sUsername
  39.         end
  40.         send( sUsers, tUser.nUserID )
  41.     end,
  42.     ["help"] = function( tUser, sContent )
  43.         send( "* Available commands:", tUser.nUserID )
  44.         local sCommands = "*"
  45.         for sCommand, fnCommand in pairs( tCommands ) do
  46.             sCommands = sCommands .. " /" .. sCommand
  47.         end
  48.         send( sCommands.." /logout", tUser.nUserID )
  49.     end,
  50. }
  51.  
  52. for nIndex, sFilename in ipairs( fs.list( "chatp" ) ) do
  53.     print("Init plugin ",sFilename)
  54.     tCommands[sFilename] = loadstring("return function(tUser, sContent) print('Command ',"..sFilename..",' was run.') "..fs.open("chatp/"..sFilename,"r").readAll().." end")()
  55. end
  56.  
  57. local sOpenedModem = nil
  58. local function openModem()
  59.     for n,sModem in ipairs( peripheral.getNames() ) do
  60.         if peripheral.getType( sModem ) == "modem" then
  61.             if not rednet.isOpen( sModem ) then
  62.                 rednet.open( sModem )
  63.                 sOpenedModem = sModem
  64.             end
  65.             return true
  66.         end
  67.     end
  68.     print( "No modems found." )
  69.     return false
  70. end
  71.  
  72. local function closeModem()
  73.     if sOpenedModem ~= nil then
  74.         rednet.close( sOpenedModem )
  75.         sOpenedModem = nil
  76.     end
  77. end
  78.  
  79. -- Colours
  80. local highlightColour, textColour
  81. if term.isColour() then
  82.     textColour = colours.white
  83.     highlightColour = colours.yellow
  84. else
  85.     textColour = colours.white
  86.     highlightColour = colours.white
  87. end
  88.  
  89. local sCommand = tArgs[1]
  90. if sCommand == "host" then
  91.     -- "chat host"
  92.     -- Get hostname
  93.     local sHostname = tArgs[2]
  94.     if sHostname == nil then
  95.         printUsage()
  96.         return
  97.     end
  98.  
  99.     -- Host server
  100.     if not openModem() then
  101.         return
  102.     end
  103.     rednet.host( "chat", sHostname )
  104.     print( "0 users connected." )
  105.  
  106.     _G.tUsers = {}
  107.     local nUsers = 0
  108.     function _G.send( sText, nUserID )
  109.         if nUserID then
  110.             local tUser = tUsers[ nUserID ]
  111.             if tUser then
  112.                 rednet.send( tUser.nID, {
  113.                     sType = "text",
  114.                     nUserID = nUserID,
  115.                     sText = sText,
  116.                 }, "chat" )
  117.             end
  118.         else
  119.             for nUserID, tUser in pairs( tUsers ) do
  120.                 rednet.send( tUser.nID, {
  121.                     sType = "text",
  122.                     nUserID = nUserID,
  123.                     sText = sText,
  124.                 }, "chat" )
  125.             end
  126.         end
  127.     end
  128.  
  129.     -- Setup ping pong
  130.     local tPingPongTimer = {}
  131.     function ping( nUserID )
  132.         local tUser = tUsers[ nUserID ]
  133.         rednet.send( tUser.nID, {
  134.             sType = "ping to client",
  135.             nUserID = nUserID,
  136.         }, "chat" )
  137.  
  138.         local timer = os.startTimer( 15 )
  139.         tUser.bPingPonged = false
  140.         tPingPongTimer[ timer ] = nUserID
  141.     end
  142.  
  143.     function printUsers()
  144.         local x,y = term.getCursorPos()
  145.         term.setCursorPos( 1, y - 1 )
  146.         term.clearLine()
  147.         if nUsers == 1 then
  148.             print( nUsers .. " user connected." )
  149.         else
  150.             print( nUsers .. " users connected." )
  151.         end
  152.     end
  153.  
  154.     -- Handle messages
  155.     local ok, error = pcall( function()
  156.         parallel.waitForAny( function()
  157.             while true do
  158.                 local sEvent, timer = os.pullEvent( "timer" )
  159.                 local nUserID = tPingPongTimer[ timer ]
  160.                 if nUserID and tUsers[ nUserID ] then
  161.                     local tUser = tUsers[ nUserID ]
  162.                     if tUser then
  163.                         if not tUser.bPingPonged then
  164.                             send( "* "..tUser.sUsername.." has timed out" )
  165.                             tUsers[ nUserID ] = nil
  166.                             nUsers = nUsers - 1
  167.                             printUsers()
  168.                         else
  169.                             ping( nUserID )
  170.                         end
  171.                     end
  172.                 end
  173.             end
  174.         end,
  175.         function()
  176.             while true do
  177.                 local nSenderID, tMessage = rednet.receive( "chat" )
  178.                 if type( tMessage ) == "table" then
  179.                     if tMessage.sType == "login" then
  180.                         -- Login from new client
  181.                         local nUserID = tMessage.nUserID
  182.                         local sUsername = tMessage.sUsername
  183.                         if nUserID and sUsername then
  184.                             tUsers[ nUserID ] = {
  185.                                 nID = nSenderID,
  186.                                 nUserID = nUserID,
  187.                                 sUsername = sUsername,
  188.                             }
  189.                             nUsers = nUsers + 1
  190.                             printUsers()
  191.                             send( "* "..sUsername.." has joined the chat" )
  192.                             ping( nUserID )
  193.                         end
  194.  
  195.                     else
  196.                         -- Something else from existing client
  197.                         local nUserID = tMessage.nUserID
  198.                         local tUser = tUsers[ nUserID ]
  199.                         if tUser and tUser.nID == nSenderID then
  200.                             if tMessage.sType == "logout" then
  201.                                 send( "* "..tUser.sUsername.." has left the chat" )
  202.                                 tUsers[ nUserID ] = nil
  203.                                 nUsers = nUsers - 1
  204.                                 printUsers()
  205.  
  206.                             elseif tMessage.sType == "chat" then
  207.                                 local sMessage = tMessage.sText
  208.                                 if sMessage then
  209.                                     local sCommand = string.match( sMessage, "^/([a-z]+)" )
  210.                                     if sCommand then
  211.                                         local fnCommand = tCommands[ sCommand ]
  212.                                         if fnCommand then
  213.                                             local sContent = string.sub( sMessage, string.len(sCommand)+3 )
  214.                                             local ok, err = pcall( function() fnCommand( tUser, sContent ) end )
  215.                                             if not ok then
  216.                                                 printError( "Command " .. sCommand .. " errored: " .. err )
  217.                                             end
  218.                                         else
  219.                                             send( "* Unrecognised command: /"..sCommand, tUser.nUserID )
  220.                                         end
  221.                                     else
  222.                                         send( "<"..tUser.sUsername.."> "..tMessage.sText )
  223.                                     end
  224.                                 end
  225.  
  226.                             elseif tMessage.sType == "ping to server" then
  227.                                 rednet.send( tUser.nID, {
  228.                                     sType = "pong to client",
  229.                                     nUserID = nUserID,
  230.                                 }, "chat" )
  231.  
  232.                             elseif tMessage.sType == "pong to server" then
  233.                                 tUser.bPingPonged = true
  234.  
  235.                             end
  236.                         end
  237.                     end
  238.                  end
  239.             end
  240.         end )
  241.     end )
  242.     if not ok then
  243.         printError( error )
  244.     end
  245.  
  246.     -- Unhost server
  247.     for nUserID, tUser in pairs( tUsers ) do
  248.         rednet.send( tUser.nID, {
  249.             sType = "kick",
  250.             nUserID = nUserID,
  251.         }, "chat" )
  252.     end
  253.     rednet.unhost( "chat" )
  254.     closeModem()
  255.  
  256. elseif sCommand == "join" then
  257.     -- "chat join"
  258.     -- Get hostname and username
  259.     local sHostname = tArgs[2]
  260.     local sUsername = tArgs[3]
  261.     if sHostname == nil or sUsername == nil then
  262.         printUsage()
  263.         return
  264.     end
  265.  
  266.     -- Connect
  267.     if not openModem() then
  268.         return
  269.     end
  270.     write( "Looking up " .. sHostname .. "... " )
  271.     local nHostID = rednet.lookup( "chat", sHostname )
  272.     if nHostID == nil then
  273.         print( "Failed." )
  274.         return
  275.     else
  276.         print( "Success." )
  277.     end
  278.  
  279.     -- Login
  280.     local nUserID = math.random( 1, 2147483647 )
  281.     rednet.send( nHostID, {
  282.         sType = "login",
  283.         nUserID = nUserID,
  284.         sUsername = sUsername,
  285.     }, "chat" )
  286.  
  287.     -- Setup ping pong
  288.     local bPingPonged = true
  289.     local pingPongTimer = os.startTimer( 0 )
  290.  
  291.     function ping()
  292.         rednet.send( nHostID, {
  293.             sType = "ping to server",
  294.             nUserID = nUserID,
  295.         }, "chat" )
  296.         bPingPonged = false
  297.         pingPongTimer = os.startTimer( 15 )
  298.     end
  299.  
  300.     -- Handle messages
  301.     local w,h = term.getSize()
  302.     local parentTerm = term.current()
  303.     local titleWindow = window.create( parentTerm, 1, 1, w, 1, true )
  304.     local historyWindow = window.create( parentTerm, 1, 2, w, h-2, true )
  305.     local promptWindow = window.create( parentTerm, 1, h, w, 1, true )
  306.     historyWindow.setCursorPos( 1, h-2 )
  307.  
  308.     term.clear()
  309.     term.setTextColour( textColour )
  310.     term.redirect( promptWindow )
  311.     promptWindow.restoreCursor()
  312.  
  313.     function drawTitle()
  314.         local x,y = titleWindow.getCursorPos()
  315.         local w,h = titleWindow.getSize()
  316.         local sTitle = sUsername.." on "..sHostname
  317.         titleWindow.setTextColour( highlightColour )
  318.         titleWindow.setCursorPos( math.floor( w/2 - string.len(sTitle)/2 ), 1 )
  319.         titleWindow.clearLine()
  320.         titleWindow.write( sTitle )
  321.         promptWindow.restoreCursor()
  322.     end
  323.  
  324.     function printMessage( sMessage )
  325.         term.redirect( historyWindow )
  326.         print()
  327.         if string.match( sMessage, "^\*" ) then
  328.             -- Information
  329.             term.setTextColour( highlightColour )
  330.             write( sMessage )
  331.             term.setTextColour( textColour )
  332.         else
  333.             -- Chat
  334.             local sUsernameBit = string.match( sMessage, "^\<[^\>]*\>" )
  335.             if sUsernameBit then
  336.                 term.setTextColour( highlightColour )
  337.                 write( sUsernameBit )
  338.                 term.setTextColour( textColour )
  339.                 write( string.sub( sMessage, string.len( sUsernameBit ) + 1 ) )
  340.             else
  341.                 write( sMessage )
  342.             end
  343.         end
  344.         term.redirect( promptWindow )
  345.         promptWindow.restoreCursor()
  346.     end
  347.  
  348.     drawTitle()
  349.  
  350.     local ok, error = pcall( function()
  351.         parallel.waitForAny( function()
  352.             while true do
  353.                 local sEvent, timer = os.pullEvent()
  354.                 if sEvent == "timer" then
  355.                     if timer == pingPongTimer then
  356.                         if not bPingPonged then
  357.                             printMessage( "Server timeout." )
  358.                             return
  359.                         else
  360.                             ping()
  361.                         end
  362.                     end
  363.  
  364.                 elseif sEvent == "term_resize" then
  365.                     local w,h = parentTerm.getSize()
  366.                     titleWindow.reposition( 1, 1, w, 1 )
  367.                     historyWindow.reposition( 1, 2, w, h-2 )
  368.                     promptWindow.reposition( 1, h, w, 1 )
  369.  
  370.                 end
  371.             end
  372.         end,
  373.         function()
  374.             while true do
  375.                 local nSenderID, tMessage = rednet.receive( "chat" )
  376.                 if nSenderID == nHostID and type( tMessage ) == "table" and tMessage.nUserID == nUserID then
  377.                     if tMessage.sType == "text" then
  378.                         local sText = tMessage.sText
  379.                         if sText then
  380.                             printMessage( sText )
  381.                         end
  382.  
  383.                     elseif tMessage.sType == "ping to client" then
  384.                         rednet.send( nSenderID, {
  385.                             sType = "pong to server",
  386.                             nUserID = nUserID,
  387.                         }, "chat" )
  388.  
  389.                     elseif tMessage.sType == "pong to client" then
  390.                         bPingPonged = true
  391.  
  392.                     elseif tMessage.sType == "kick" then
  393.                         return
  394.  
  395.                     end
  396.                 end
  397.             end
  398.         end,
  399.         function()
  400.             local tSendHistory = {}
  401.             while true do
  402.                 promptWindow.setCursorPos( 1,1 )
  403.                 promptWindow.clearLine()
  404.                 promptWindow.setTextColor( highlightColour )
  405.                 promptWindow.write( ": ")
  406.                 promptWindow.setTextColor( textColour )
  407.  
  408.                 local sChat = read( nil, tSendHistory )
  409.                 if string.match( sChat, "^/logout" ) then
  410.                     break
  411.                 else
  412.                     rednet.send( nHostID, {
  413.                         sType = "chat",
  414.                         nUserID = nUserID,
  415.                         sText = sChat,
  416.                     }, "chat" )
  417.                     table.insert( tSendHistory, sChat )
  418.                 end
  419.             end
  420.         end )
  421.     end )
  422.  
  423.     -- Close the windows
  424.     term.redirect( parentTerm )
  425.  
  426.     -- Print error notice
  427.     local w,h = term.getSize()
  428.     term.setCursorPos( 1, h )
  429.     term.clearLine()
  430.     term.setCursorBlink( false )
  431.     if not ok then
  432.         printError( error )
  433.     end
  434.  
  435.     -- Logout
  436.     rednet.send( nHostID, {
  437.         sType = "logout",
  438.         nUserID = nUserID,
  439.     }, "chat" )
  440.     closeModem()
  441.  
  442.     -- Print disconnection notice
  443.     print( "Disconnected." )
  444.  
  445. else
  446.     -- "chat somethingelse"
  447.     printUsage()
  448.  
  449. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement