smilerryan

[LUA] ComputerCraft RedNetChat

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