Derek1017

Rednet Chat

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