Fleethan

components 4 Messenges

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