Advertisement
gpgautier

RedNetChat v6 - A ComputerCraft chat program

Nov 24th, 2012
6,861
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.34 KB | None | 0 0
  1. -- [ --------------------------------------------------------------------- ] --
  2. -- [ RedNetChat - A public chat program for ComputerCraft                  ] --
  3. -- [ Created by gpgautier (ign gpgauier), 2012                             ] --
  4. -- [ Licence: Creative Commons Attribution-ShareAlike 3.0 Unported License ] --
  5. -- [ ComputerCraft version: 1.3 and up                                     ] --
  6. -- [ Posted to:                                                            ] --
  7. -- [ GitHub: https://github.com/gpgautier/RedNetChat                       ] --
  8. -- [ Pastebin: http://pastebin.com/JDU4wJxX                                ] --
  9. -- [ --------------------------------------------------------------------- ] --
  10.  
  11. local VERSION = "0.6"
  12. local MODEM = nil
  13. local NICKNAME = nil
  14. local ACTIVE = false
  15. local BUFFER = {}
  16. local POINTER = 0
  17. local ONLINE = {}
  18. local ISONLINE = false
  19. local ID = os.computerID()
  20. local LAST_MSG_TARGET = nil
  21. local CHANNEL = 1
  22. local SCROLL_POINTER = POINTER
  23. local WIDTH, HEIGHT = term.getSize()
  24. local LINES = HEIGHT - 6
  25. local START_LINE = 5
  26. local OPERATOR = "RNC"
  27.  
  28. -- [ --------------------------------------------------------------------- ] --
  29.  
  30. -- Split a string
  31. function split(str, pat)
  32.     local t = {}  -- NOTE: use {n = 0} in Lua-5.0
  33.     if str ~= nil then
  34.        local fpat = "(.-)" .. pat
  35.        local last_end = 1
  36.        local s, e, cap = str:find(fpat, 1)
  37.        while s do
  38.           if s ~= 1 or cap ~= "" then
  39.             table.insert(t,cap)
  40.           end
  41.           last_end = e+1
  42.           s, e, cap = str:find(fpat, last_end)
  43.        end
  44.        if last_end <= #str then
  45.           cap = str:sub(last_end)
  46.           table.insert(t, cap)
  47.        end
  48.     else
  49.         print("##ERROR failed to split ["..str.."] by:"..pat)
  50.     end
  51.     return t
  52. end
  53.  
  54. -- Log a message to file
  55. function log(message)
  56.   local file = io.open("rednetchat.log", "a")
  57.   file:write("\n" .. message)
  58.   file:close()
  59. end
  60.  
  61. -- Application entry
  62. function main()
  63.   term.clear()
  64.   term.setCursorPos(1, 1)
  65.  
  66.   if not setPeripherals() then
  67.     print("[FATAL ERROR] Not able to setup peripherals.")
  68.     return false
  69.   end
  70.  
  71.   welcome()
  72. end
  73.  
  74. -- Set the attached peripherals. Opens rednet modem and warps monitor
  75. function setPeripherals()
  76.   local i, side
  77.  
  78.   for i, side in pairs(rs.getSides()) do
  79.     if peripheral.isPresent(side) then
  80.       if peripheral.getType(side) == "modem" then
  81.         MODEM = side
  82.         if not rednet.isOpen(side) then
  83.           rednet.open(MODEM)
  84.         end
  85.       end
  86.     end
  87.   end
  88.  
  89.   -- Exit with a fatal error when modem not found
  90.   if MODEM == nil then
  91.     print("[FATAL ERROR] No modem was detected. Plase attach a modem on any side.")
  92.     return false
  93.   end
  94.  
  95.   return true
  96. end
  97.  
  98. -- Start the welcome screen
  99. function welcome()
  100.   local x, y
  101.  
  102.   term.clear()
  103.   writeHeader()
  104.  
  105.   print("")
  106.   print("")
  107.   print("Enter a nickname and press [enter].")
  108.   print("")
  109.   term.write("Nickname: ")
  110.  
  111.   x, y = term.getCursorPos()
  112.  
  113.   while NICKNAME == nil or NICKNAME == "" do
  114.     term.setCursorPos(x, y)
  115.     NICKNAME = read()
  116.     execute("/online")
  117.     appendBuffer("[" .. OPERATOR .. "]: Type /help for a list of commands")
  118.   end
  119.  
  120.   start()
  121. end
  122.  
  123. -- Writes the screen header
  124. function writeHeader()
  125.   local col
  126.  
  127.   term.setCursorPos(1, 1)
  128.   term.write("RedNet Chat " .. VERSION .. "")
  129.   term.setCursorPos(1, 2)
  130.  
  131.   for col = 1, WIDTH do
  132.     term.write("-")
  133.   end
  134. end
  135.  
  136. -- Writes the list of online users
  137. function writeOnlineList()
  138.   local i, v, count, x, y, col
  139.  
  140.   count = 0
  141.  
  142.   x, y = term.getCursorPos()
  143.  
  144.   term.setCursorPos(1, HEIGHT - 1)
  145.  
  146.   for col = 1, WIDTH do
  147.     term.write("-")
  148.   end
  149.  
  150.   term.setCursorPos(1, HEIGHT)
  151.   term.clearLine()
  152.   term.write("Online: ")
  153.  
  154.   for i, v in pairs(ONLINE) do
  155.     if count == 0 then
  156.       term.write(i)
  157.     else
  158.       term.write(", " .. i)
  159.     end
  160.  
  161.     count = count + 1
  162.   end
  163.  
  164.   if count == 0 then
  165.     term.write("Nobody online in channel " .. CHANNEL)
  166.   end
  167.  
  168.   term.setCursorPos(x, y)
  169. end
  170.  
  171. -- Start the chat
  172. function start()
  173.   term.clear()
  174.   writeHeader()
  175.   writeOnlineList()
  176.  
  177.   ACTIVE = true
  178.  
  179.   showBuffer()
  180.  
  181.   parallel.waitForAll(input, watchEvents)
  182. end
  183.  
  184. -- Stop the application
  185. function stop()
  186.   ACTIVE = false
  187. end
  188.  
  189. -- Reset the application
  190. function reset()
  191.   execute("/offline")
  192.  
  193.   if rednet.isOpen(MODEM) then
  194.     rednet.close(MODEM)
  195.   end
  196.  
  197.   sleep(1.5)
  198.   os.reboot()
  199. end
  200.  
  201. -- Watch all input to provide possible shortcuts (for example usernames)
  202. function watchEvents()
  203.   local type, param, param2, param3, i, v
  204.  
  205.   while ACTIVE do
  206.     type, param, param2, param3 = os.pullEvent()
  207.  
  208.     if type == "key" then
  209.       if param == 200 then -- up
  210.         scroll(-1)
  211.       elseif param == 208 then -- down
  212.         scroll(1)
  213.       elseif param == 201 then -- pageup
  214.         scroll(-12)
  215.       elseif param == 209 then -- pagedown
  216.         scroll(12)
  217.       --else
  218.       --  appendBuffer(tostring(param))
  219.       end
  220.     elseif type == "mouse_scroll" then
  221.       if param == -1 then
  222.         scroll(-1)
  223.       else
  224.         scroll(1)
  225.       end
  226.     elseif type == "rednet_message" then
  227.       receive(param2)
  228.     end
  229.   end
  230. end
  231.  
  232. -- Scroll through the chat
  233. function scroll(amount)
  234.   SCROLL_POINTER = SCROLL_POINTER + amount
  235.   showBuffer()
  236. end
  237.  
  238. -- Handle input from the prompt
  239. function input()
  240.   local message, col
  241.  
  242.   term.setCursorPos(1, 4)
  243.  
  244.   for col = 1, WIDTH do
  245.     term.write("-")
  246.   end
  247.  
  248.   while ACTIVE do
  249.     term.setCursorPos(1, 3)
  250.     term.clearLine()
  251.     term.write("[" .. CHANNEL .. "] > ")
  252.  
  253.     message = read()
  254.  
  255.     if message ~= nil and message ~= "" then
  256.       execute(message, "local")
  257.     end
  258.   end
  259. end
  260.  
  261. -- Send a message
  262. function send(message, target)
  263.   local request, serialized, x, encrypted
  264.  
  265.   request = {protocol = "rnc", nickname = NICKNAME, sender = ID, target = target, channel = CHANNEL, message = message}
  266.   serialized = textutils.serialize(request)
  267.  
  268.   encrypted = ""
  269.   for x = 1, #serialized do
  270.     encrypted = encrypted .. string.char(serialized:byte(x) + 1)
  271.   end
  272.  
  273.   if request.target ~= nil then      
  274.     rednet.send(request.target, encrypted)
  275.   else
  276.     rednet.broadcast(encrypted)
  277.   end
  278. end
  279.  
  280. -- Recieve a message
  281. function receive(message)
  282.   local request, decrypted, x
  283.  
  284.   if message ~= nil and message ~= "" then
  285.  
  286.     decrypted = ""
  287.     for x = 1, #message do
  288.       decrypted = decrypted .. string.char(message:byte(x) - 1)
  289.     end
  290.  
  291.     request = textutils.unserialize(decrypted)
  292.  
  293.     if request.protocol == "rnc" and request.channel == CHANNEL then
  294.       if request.nickname ~= nil and request.nickname ~= "" then
  295.         execute(request, "remote")
  296.       end
  297.     end
  298.   end
  299. end
  300.  
  301. -- Execute a command or add a chat message
  302. function execute(message, source)
  303.   local command, splitCommand, nickname, id, body, onlineUser
  304.  
  305.   if message.nickname ~= nil then
  306.     executeRemote(message)
  307.     return
  308.   end
  309.  
  310.   if message:sub(0, 1) == "/" then
  311.       command = message:sub(2)
  312.  
  313.       if command == "quit"
  314.           or command == "reset"
  315.           or command == "restart"
  316.           or command == "reboot"
  317.           or command == "stop"
  318.         then
  319.           appendBuffer("[" .. OPERATOR .. "]: Stopping application")
  320.           reset()
  321.       elseif command == "online" then
  322.         if not ISONLINE then
  323.           send("/online")
  324.           putOnline()
  325.           appendBuffer("[" .. OPERATOR .. "]: You are now online")
  326.           ISONLINE = true
  327.         else
  328.           appendBuffer("[" .. OPERATOR .. "]: You are already online")
  329.         end
  330.       elseif command == "offline" then
  331.         if ISONLINE then
  332.           send("/offline")
  333.           takeOffline()
  334.           appendBuffer("[" .. OPERATOR .. "]: You are now offline")
  335.           ISONLINE = false
  336.         else
  337.           appendBuffer("[" .. OPERATOR .. "]: You are already offline")
  338.         end
  339.       elseif command:sub(0, 5) == "nick " then
  340.         takeOffline()
  341.         NICKNAME = command:sub(6)
  342.         putOnline()
  343.         appendBuffer("[" .. OPERATOR .. "]: Your nickname has been changed")
  344.       elseif command:sub(0, 5) == "slap " then
  345.         appendBuffer(command:sub(6) .. " was slapped by " .. NICKNAME)
  346.       elseif command:sub(0, 4) == "msg " then
  347.         splitCommand = split(command:sub(5), "%s")
  348.  
  349.         onlineUser = false
  350.  
  351.         for nickname, id in pairs(ONLINE) do
  352.           if nickname == splitCommand[1] then
  353.             body = command:sub(5 + splitCommand[1]:len() + 1)
  354.             send(body, id)
  355.             appendBuffer(NICKNAME .. " > " .. nickname .. ": " .. body)
  356.             onlineUser = true
  357.             LAST_MSG_TARGET = nickname
  358.           end
  359.         end
  360.  
  361.         if not onlineUser then
  362.             appendBuffer("[" .. OPERATOR .. "]: User " .. splitCommand[1] .. " is not online")
  363.         end
  364.       elseif command:sub(0, 2) == "r " then
  365.         if LAST_MSG_TARGET ~= nil then
  366.           execute("/msg " .. LAST_MSG_TARGET .. " " .. command:sub(3), "local")
  367.         else
  368.           appendBuffer("[" .. OPERATOR .. "]: No valid user for message")
  369.         end
  370.       elseif command:sub(0, 5) == "join " then
  371.         if CHANNEL ~= tonumber(command:sub(6)) then
  372.           execute("/offline")
  373.           CHANNEL = tonumber(command:sub(6))
  374.           execute("/online")
  375.           appendBuffer("[" .. OPERATOR .. "]: Joined channel " .. CHANNEL)
  376.         else
  377.           appendBuffer("[" .. OPERATOR .. "]: Already in channel " .. CHANNEL)
  378.         end
  379.       elseif command == "help" then
  380.         appendBuffer("[" .. OPERATOR .. "] Commands:")
  381.         appendBuffer("/quit : Exit the chat")
  382.         appendBuffer("/msg <nickname> <message> : Send a private message")
  383.         appendBuffer("/r <message> : Reply to a private message")
  384.         appendBuffer("/join <channel> : Switch channel")
  385.       else
  386.         appendBuffer("[" .. OPERATOR .. "]: Unknown command")
  387.       end
  388.      
  389.       return
  390.   end
  391.  
  392.   appendBuffer(NICKNAME .. ": " .. message)
  393.   send(message)
  394. end
  395.  
  396. --
  397. function putOnline(nickname, id)
  398.   if nickname == nil or id == nil then
  399.     nickname = NICKNAME
  400.     id = ID
  401.   end
  402.  
  403.   ONLINE[nickname] = id
  404.  
  405.   writeOnlineList()
  406. end
  407.  
  408. --
  409. function takeOffline(nickname, id)
  410.   if nickname == nil or id == nil then
  411.     nickname = NICKNAME
  412.     id = ID
  413.   end
  414.  
  415.   ONLINE[nickname] = nil
  416.  
  417.   writeOnlineList()
  418. end
  419.  
  420. --
  421. function executeRemote(request)
  422.   local command
  423.  
  424.   if request.message:sub(0, 1) == "/" then
  425.     command = request.message:sub(2)
  426.  
  427.     if command == "online" then
  428.       putOnline(request.nickname, request.sender)
  429.       appendBuffer("[" .. OPERATOR .. "]: " .. request.nickname .. " is now online")
  430.       send("/metoo")
  431.     elseif command == "offline" then
  432.       takeOffline(request.nickname, request.sender)
  433.       appendBuffer("[" .. OPERATOR .. "]: " .. request.nickname .. " is now offline")
  434.     elseif command == "metoo" then
  435.       putOnline(request.nickname, request.sender)
  436.     end
  437.     return
  438.   end
  439.  
  440.   if request.target ~= nil then
  441.     appendBuffer(request.nickname .. " > " .. NICKNAME .. ": " .. request.message)
  442.     LAST_MSG_TARGET = request.nickname
  443.   else
  444.     appendBuffer(request.nickname .. ": " .. request.message)
  445.   end
  446. end
  447.  
  448. --
  449. function appendBuffer(message)
  450.   local length
  451.  
  452.   length = message:len()
  453.  
  454.   if length > WIDTH then
  455.     table.insert(BUFFER, message:sub(1, WIDTH))
  456.     POINTER = POINTER + 1
  457.     appendBuffer(message:sub(WIDTH + 1))
  458.   else
  459.     table.insert(BUFFER, message)
  460.     POINTER = POINTER + 1
  461.   end
  462.  
  463.   SCROLL_POINTER = POINTER
  464.  
  465.   showBuffer()
  466. end
  467.  
  468. --
  469. function showBuffer()
  470.   local i, line, bufferPointer, x, y, pointer
  471.  
  472.   pointer = SCROLL_POINTER
  473.  
  474.   if pointer == 0 then
  475.     return
  476.   elseif SCROLL_POINTER > POINTER then
  477.     SCROLL_POINTER = POINTER
  478.     pointer = POINTER
  479.   elseif POINTER < LINES + 1 then
  480.     SCROLL_POINTER = POINTER
  481.     pointer = POINTER
  482.   elseif POINTER > LINES and SCROLL_POINTER < LINES then
  483.     SCROLL_POINTER = LINES
  484.     pointer = SCROLL_POINTER
  485.   end
  486.  
  487.   x, y = term.getCursorPos()
  488.  
  489.   line = START_LINE
  490.  
  491.   bufferPointer = -(LINES - 1 - pointer)
  492.  
  493.   for i = bufferPointer, bufferPointer + (LINES - 1) do
  494.     term.setCursorPos(1, line)
  495.     term.clearLine()
  496.    
  497.     if BUFFER[i] ~= nil then
  498.       term.write(tostring(BUFFER[i]))
  499.     end
  500.    
  501.     line = line + 1
  502.   end
  503.  
  504.   term.setCursorPos(x, y)
  505. end
  506.  
  507. -- Fire up the application
  508. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement