View difference between Paste ID: RFBig4M1 and JDU4wJxX
SHOW: | | - or go back to the newest paste.
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()