-- [ --------------------------------------------------------------------- ] --
-- [ RedNetChat - A public chat program for ComputerCraft ] --
-- [ Created by gpgautier (ign gpgauier), 2012 ] --
-- [ Licence: Creative Commons Attribution-ShareAlike 3.0 Unported License ] --
-- [ ComputerCraft version: 1.3 and up ] --
-- [ Posted to: ] --
-- [ GitHub: https://github.com/gpgautier/RedNetChat ] --
-- [ Pastebin: http://pastebin.com/JDU4wJxX ] --
-- [ --------------------------------------------------------------------- ] --
local VERSION = "0.6"
local MODEM = nil
local NICKNAME = nil
local ACTIVE = false
local BUFFER = {}
local POINTER = 0
local ONLINE = {}
local ISONLINE = false
local ID = os.computerID()
local LAST_MSG_TARGET = nil
local CHANNEL = 1
local SCROLL_POINTER = POINTER
local WIDTH, HEIGHT = term.getSize()
local LINES = HEIGHT - 6
local START_LINE = 5
local OPERATOR = "RNC"
-- [ --------------------------------------------------------------------- ] --
-- Split a string
function split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
if str ~= nil then
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
else
print("##ERROR failed to split ["..str.."] by:"..pat)
end
return t
end
-- Log a message to file
function log(message)
local file = io.open("rednetchat.log", "a")
file:write("\n" .. message)
file:close()
end
-- Application entry
function main()
term.clear()
term.setCursorPos(1, 1)
if not setPeripherals() then
print("[FATAL ERROR] Not able to setup peripherals.")
return false
end
welcome()
end
-- Set the attached peripherals. Opens rednet modem and warps monitor
function setPeripherals()
local i, side
for i, side in pairs(rs.getSides()) do
if peripheral.isPresent(side) then
if peripheral.getType(side) == "modem" then
MODEM = side
if not rednet.isOpen(side) then
rednet.open(MODEM)
end
end
end
end
-- Exit with a fatal error when modem not found
if MODEM == nil then
print("[FATAL ERROR] No modem was detected. Plase attach a modem on any side.")
return false
end
return true
end
-- Start the welcome screen
function welcome()
local x, y
term.clear()
writeHeader()
print("")
print("")
print("Enter a nickname and press [enter].")
print("")
term.write("Nickname: ")
x, y = term.getCursorPos()
while NICKNAME == nil or NICKNAME == "" do
term.setCursorPos(x, y)
NICKNAME = read()
execute("/online")
appendBuffer("[" .. OPERATOR .. "]: Type /help for a list of commands")
end
start()
end
-- Writes the screen header
function writeHeader()
local col
term.setCursorPos(1, 1)
term.write("RedNet Chat " .. VERSION .. "")
term.setCursorPos(1, 2)
for col = 1, WIDTH do
term.write("-")
end
end
-- Writes the list of online users
function writeOnlineList()
local i, v, count, x, y, col
count = 0
x, y = term.getCursorPos()
term.setCursorPos(1, HEIGHT - 1)
for col = 1, WIDTH do
term.write("-")
end
term.setCursorPos(1, HEIGHT)
term.clearLine()
term.write("Online: ")
for i, v in pairs(ONLINE) do
if count == 0 then
term.write(i)
else
term.write(", " .. i)
end
count = count + 1
end
if count == 0 then
term.write("Nobody online in channel " .. CHANNEL)
end
term.setCursorPos(x, y)
end
-- Start the chat
function start()
term.clear()
writeHeader()
writeOnlineList()
ACTIVE = true
showBuffer()
parallel.waitForAll(input, watchEvents)
end
-- Stop the application
function stop()
ACTIVE = false
end
-- Reset the application
function reset()
execute("/offline")
if rednet.isOpen(MODEM) then
rednet.close(MODEM)
end
sleep(1.5)
os.reboot()
end
-- Watch all input to provide possible shortcuts (for example usernames)
function watchEvents()
local type, param, param2, param3, i, v
while ACTIVE do
type, param, param2, param3 = os.pullEvent()
if type == "key" then
if param == 200 then -- up
scroll(-1)
elseif param == 208 then -- down
scroll(1)
elseif param == 201 then -- pageup
scroll(-12)
elseif param == 209 then -- pagedown
scroll(12)
--else
-- appendBuffer(tostring(param))
end
elseif type == "mouse_scroll" then
if param == -1 then
scroll(-1)
else
scroll(1)
end
elseif type == "rednet_message" then
receive(param2)
end
end
end
-- Scroll through the chat
function scroll(amount)
SCROLL_POINTER = SCROLL_POINTER + amount
showBuffer()
end
-- Handle input from the prompt
function input()
local message, col
term.setCursorPos(1, 4)
for col = 1, WIDTH do
term.write("-")
end
while ACTIVE do
term.setCursorPos(1, 3)
term.clearLine()
term.write("[" .. CHANNEL .. "] > ")
message = read()
if message ~= nil and message ~= "" then
execute(message, "local")
end
end
end
-- Send a message
function send(message, target)
local request, serialized, x, encrypted
request = {protocol = "rnc", nickname = NICKNAME, sender = ID, target = target, channel = CHANNEL, message = message}
serialized = textutils.serialize(request)
encrypted = ""
for x = 1, #serialized do
encrypted = encrypted .. string.char(serialized:byte(x) + 1)
end
if request.target ~= nil then
rednet.send(request.target, encrypted)
else
rednet.broadcast(encrypted)
end
end
-- Recieve a message
function receive(message)
local request, decrypted, x
if message ~= nil and message ~= "" then
decrypted = ""
for x = 1, #message do
decrypted = decrypted .. string.char(message:byte(x) - 1)
end
request = textutils.unserialize(decrypted)
if request.protocol == "rnc" and request.channel == CHANNEL then
if request.nickname ~= nil and request.nickname ~= "" then
execute(request, "remote")
end
end
end
end
-- Execute a command or add a chat message
function execute(message, source)
local command, splitCommand, nickname, id, body, onlineUser
if message.nickname ~= nil then
executeRemote(message)
return
end
if message:sub(0, 1) == "/" then
command = message:sub(2)
if command == "quit"
or command == "reset"
or command == "restart"
or command == "reboot"
or command == "stop"
then
appendBuffer("[" .. OPERATOR .. "]: Stopping application")
reset()
elseif command == "online" then
if not ISONLINE then
send("/online")
putOnline()
appendBuffer("[" .. OPERATOR .. "]: You are now online")
ISONLINE = true
else
appendBuffer("[" .. OPERATOR .. "]: You are already online")
end
elseif command == "offline" then
if ISONLINE then
send("/offline")
takeOffline()
appendBuffer("[" .. OPERATOR .. "]: You are now offline")
ISONLINE = false
else
appendBuffer("[" .. OPERATOR .. "]: You are already offline")
end
elseif command:sub(0, 5) == "nick " then
takeOffline()
NICKNAME = command:sub(6)
putOnline()
appendBuffer("[" .. OPERATOR .. "]: Your nickname has been changed")
elseif command:sub(0, 5) == "slap " then
appendBuffer(command:sub(6) .. " was slapped by " .. NICKNAME)
elseif command:sub(0, 4) == "msg " then
splitCommand = split(command:sub(5), "%s")
onlineUser = false
for nickname, id in pairs(ONLINE) do
if nickname == splitCommand[1] then
body = command:sub(5 + splitCommand[1]:len() + 1)
send(body, id)
appendBuffer(NICKNAME .. " > " .. nickname .. ": " .. body)
onlineUser = true
LAST_MSG_TARGET = nickname
end
end
if not onlineUser then
appendBuffer("[" .. OPERATOR .. "]: User " .. splitCommand[1] .. " is not online")
end
elseif command:sub(0, 2) == "r " then
if LAST_MSG_TARGET ~= nil then
execute("/msg " .. LAST_MSG_TARGET .. " " .. command:sub(3), "local")
else
appendBuffer("[" .. OPERATOR .. "]: No valid user for message")
end
elseif command:sub(0, 5) == "join " then
if CHANNEL ~= tonumber(command:sub(6)) then
execute("/offline")
CHANNEL = tonumber(command:sub(6))
execute("/online")
appendBuffer("[" .. OPERATOR .. "]: Joined channel " .. CHANNEL)
else
appendBuffer("[" .. OPERATOR .. "]: Already in channel " .. CHANNEL)
end
elseif command == "help" then
appendBuffer("[" .. OPERATOR .. "] Commands:")
appendBuffer("/quit : Exit the chat")
appendBuffer("/msg <nickname> <message> : Send a private message")
appendBuffer("/r <message> : Reply to a private message")
appendBuffer("/join <channel> : Switch channel")
else
appendBuffer("[" .. OPERATOR .. "]: Unknown command")
end
return
end
appendBuffer(NICKNAME .. ": " .. message)
send(message)
end
--
function putOnline(nickname, id)
if nickname == nil or id == nil then
nickname = NICKNAME
id = ID
end
ONLINE[nickname] = id
writeOnlineList()
end
--
function takeOffline(nickname, id)
if nickname == nil or id == nil then
nickname = NICKNAME
id = ID
end
ONLINE[nickname] = nil
writeOnlineList()
end
--
function executeRemote(request)
local command
if request.message:sub(0, 1) == "/" then
command = request.message:sub(2)
if command == "online" then
putOnline(request.nickname, request.sender)
appendBuffer("[" .. OPERATOR .. "]: " .. request.nickname .. " is now online")
send("/metoo")
elseif command == "offline" then
takeOffline(request.nickname, request.sender)
appendBuffer("[" .. OPERATOR .. "]: " .. request.nickname .. " is now offline")
elseif command == "metoo" then
putOnline(request.nickname, request.sender)
end
return
end
if request.target ~= nil then
appendBuffer(request.nickname .. " > " .. NICKNAME .. ": " .. request.message)
LAST_MSG_TARGET = request.nickname
else
appendBuffer(request.nickname .. ": " .. request.message)
end
end
--
function appendBuffer(message)
local length
length = message:len()
if length > WIDTH then
table.insert(BUFFER, message:sub(1, WIDTH))
POINTER = POINTER + 1
appendBuffer(message:sub(WIDTH + 1))
else
table.insert(BUFFER, message)
POINTER = POINTER + 1
end
SCROLL_POINTER = POINTER
showBuffer()
end
--
function showBuffer()
local i, line, bufferPointer, x, y, pointer
pointer = SCROLL_POINTER
if pointer == 0 then
return
elseif SCROLL_POINTER > POINTER then
SCROLL_POINTER = POINTER
pointer = POINTER
elseif POINTER < LINES + 1 then
SCROLL_POINTER = POINTER
pointer = POINTER
elseif POINTER > LINES and SCROLL_POINTER < LINES then
SCROLL_POINTER = LINES
pointer = SCROLL_POINTER
end
x, y = term.getCursorPos()
line = START_LINE
bufferPointer = -(LINES - 1 - pointer)
for i = bufferPointer, bufferPointer + (LINES - 1) do
term.setCursorPos(1, line)
term.clearLine()
if BUFFER[i] ~= nil then
term.write(tostring(BUFFER[i]))
end
line = line + 1
end
term.setCursorPos(x, y)
end
-- Fire up the application
main()
-- [ RedNetChat - A public chat program for ComputerCraft ] --
-- [ Created by gpgautier (ign gpgauier), 2012 ] --
-- [ Licence: Creative Commons Attribution-ShareAlike 3.0 Unported License ] --
-- [ ComputerCraft version: 1.3 and up ] --
-- [ Posted to: ] --
-- [ GitHub: https://github.com/gpgautier/RedNetChat ] --
-- [ Pastebin: http://pastebin.com/JDU4wJxX ] --
-- [ --------------------------------------------------------------------- ] --
local VERSION = "0.6"
local MODEM = nil
local NICKNAME = nil
local ACTIVE = false
local BUFFER = {}
local POINTER = 0
local ONLINE = {}
local ISONLINE = false
local ID = os.computerID()
local LAST_MSG_TARGET = nil
local CHANNEL = 1
local SCROLL_POINTER = POINTER
local WIDTH, HEIGHT = term.getSize()
local LINES = HEIGHT - 6
local START_LINE = 5
local OPERATOR = "RNC"
-- [ --------------------------------------------------------------------- ] --
-- Split a string
function split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
if str ~= nil then
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
else
print("##ERROR failed to split ["..str.."] by:"..pat)
end
return t
end
-- Log a message to file
function log(message)
local file = io.open("rednetchat.log", "a")
file:write("\n" .. message)
file:close()
end
-- Application entry
function main()
term.clear()
term.setCursorPos(1, 1)
if not setPeripherals() then
print("[FATAL ERROR] Not able to setup peripherals.")
return false
end
welcome()
end
-- Set the attached peripherals. Opens rednet modem and warps monitor
function setPeripherals()
local i, side
for i, side in pairs(rs.getSides()) do
if peripheral.isPresent(side) then
if peripheral.getType(side) == "modem" then
MODEM = side
if not rednet.isOpen(side) then
rednet.open(MODEM)
end
end
end
end
-- Exit with a fatal error when modem not found
if MODEM == nil then
print("[FATAL ERROR] No modem was detected. Plase attach a modem on any side.")
return false
end
return true
end
-- Start the welcome screen
function welcome()
local x, y
term.clear()
writeHeader()
print("")
print("")
print("Enter a nickname and press [enter].")
print("")
term.write("Nickname: ")
x, y = term.getCursorPos()
while NICKNAME == nil or NICKNAME == "" do
term.setCursorPos(x, y)
NICKNAME = read()
execute("/online")
appendBuffer("[" .. OPERATOR .. "]: Type /help for a list of commands")
end
start()
end
-- Writes the screen header
function writeHeader()
local col
term.setCursorPos(1, 1)
term.write("RedNet Chat " .. VERSION .. "")
term.setCursorPos(1, 2)
for col = 1, WIDTH do
term.write("-")
end
end
-- Writes the list of online users
function writeOnlineList()
local i, v, count, x, y, col
count = 0
x, y = term.getCursorPos()
term.setCursorPos(1, HEIGHT - 1)
for col = 1, WIDTH do
term.write("-")
end
term.setCursorPos(1, HEIGHT)
term.clearLine()
term.write("Online: ")
for i, v in pairs(ONLINE) do
if count == 0 then
term.write(i)
else
term.write(", " .. i)
end
count = count + 1
end
if count == 0 then
term.write("Nobody online in channel " .. CHANNEL)
end
term.setCursorPos(x, y)
end
-- Start the chat
function start()
term.clear()
writeHeader()
writeOnlineList()
ACTIVE = true
showBuffer()
parallel.waitForAll(input, watchEvents)
end
-- Stop the application
function stop()
ACTIVE = false
end
-- Reset the application
function reset()
execute("/offline")
if rednet.isOpen(MODEM) then
rednet.close(MODEM)
end
sleep(1.5)
os.reboot()
end
-- Watch all input to provide possible shortcuts (for example usernames)
function watchEvents()
local type, param, param2, param3, i, v
while ACTIVE do
type, param, param2, param3 = os.pullEvent()
if type == "key" then
if param == 200 then -- up
scroll(-1)
elseif param == 208 then -- down
scroll(1)
elseif param == 201 then -- pageup
scroll(-12)
elseif param == 209 then -- pagedown
scroll(12)
--else
-- appendBuffer(tostring(param))
end
elseif type == "mouse_scroll" then
if param == -1 then
scroll(-1)
else
scroll(1)
end
elseif type == "rednet_message" then
receive(param2)
end
end
end
-- Scroll through the chat
function scroll(amount)
SCROLL_POINTER = SCROLL_POINTER + amount
showBuffer()
end
-- Handle input from the prompt
function input()
local message, col
term.setCursorPos(1, 4)
for col = 1, WIDTH do
term.write("-")
end
while ACTIVE do
term.setCursorPos(1, 3)
term.clearLine()
term.write("[" .. CHANNEL .. "] > ")
message = read()
if message ~= nil and message ~= "" then
execute(message, "local")
end
end
end
-- Send a message
function send(message, target)
local request, serialized, x, encrypted
request = {protocol = "rnc", nickname = NICKNAME, sender = ID, target = target, channel = CHANNEL, message = message}
serialized = textutils.serialize(request)
encrypted = ""
for x = 1, #serialized do
encrypted = encrypted .. string.char(serialized:byte(x) + 1)
end
if request.target ~= nil then
rednet.send(request.target, encrypted)
else
rednet.broadcast(encrypted)
end
end
-- Recieve a message
function receive(message)
local request, decrypted, x
if message ~= nil and message ~= "" then
decrypted = ""
for x = 1, #message do
decrypted = decrypted .. string.char(message:byte(x) - 1)
end
request = textutils.unserialize(decrypted)
if request.protocol == "rnc" and request.channel == CHANNEL then
if request.nickname ~= nil and request.nickname ~= "" then
execute(request, "remote")
end
end
end
end
-- Execute a command or add a chat message
function execute(message, source)
local command, splitCommand, nickname, id, body, onlineUser
if message.nickname ~= nil then
executeRemote(message)
return
end
if message:sub(0, 1) == "/" then
command = message:sub(2)
if command == "quit"
or command == "reset"
or command == "restart"
or command == "reboot"
or command == "stop"
then
appendBuffer("[" .. OPERATOR .. "]: Stopping application")
reset()
elseif command == "online" then
if not ISONLINE then
send("/online")
putOnline()
appendBuffer("[" .. OPERATOR .. "]: You are now online")
ISONLINE = true
else
appendBuffer("[" .. OPERATOR .. "]: You are already online")
end
elseif command == "offline" then
if ISONLINE then
send("/offline")
takeOffline()
appendBuffer("[" .. OPERATOR .. "]: You are now offline")
ISONLINE = false
else
appendBuffer("[" .. OPERATOR .. "]: You are already offline")
end
elseif command:sub(0, 5) == "nick " then
takeOffline()
NICKNAME = command:sub(6)
putOnline()
appendBuffer("[" .. OPERATOR .. "]: Your nickname has been changed")
elseif command:sub(0, 5) == "slap " then
appendBuffer(command:sub(6) .. " was slapped by " .. NICKNAME)
elseif command:sub(0, 4) == "msg " then
splitCommand = split(command:sub(5), "%s")
onlineUser = false
for nickname, id in pairs(ONLINE) do
if nickname == splitCommand[1] then
body = command:sub(5 + splitCommand[1]:len() + 1)
send(body, id)
appendBuffer(NICKNAME .. " > " .. nickname .. ": " .. body)
onlineUser = true
LAST_MSG_TARGET = nickname
end
end
if not onlineUser then
appendBuffer("[" .. OPERATOR .. "]: User " .. splitCommand[1] .. " is not online")
end
elseif command:sub(0, 2) == "r " then
if LAST_MSG_TARGET ~= nil then
execute("/msg " .. LAST_MSG_TARGET .. " " .. command:sub(3), "local")
else
appendBuffer("[" .. OPERATOR .. "]: No valid user for message")
end
elseif command:sub(0, 5) == "join " then
if CHANNEL ~= tonumber(command:sub(6)) then
execute("/offline")
CHANNEL = tonumber(command:sub(6))
execute("/online")
appendBuffer("[" .. OPERATOR .. "]: Joined channel " .. CHANNEL)
else
appendBuffer("[" .. OPERATOR .. "]: Already in channel " .. CHANNEL)
end
elseif command == "help" then
appendBuffer("[" .. OPERATOR .. "] Commands:")
appendBuffer("/quit : Exit the chat")
appendBuffer("/msg <nickname> <message> : Send a private message")
appendBuffer("/r <message> : Reply to a private message")
appendBuffer("/join <channel> : Switch channel")
else
appendBuffer("[" .. OPERATOR .. "]: Unknown command")
end
return
end
appendBuffer(NICKNAME .. ": " .. message)
send(message)
end
--
function putOnline(nickname, id)
if nickname == nil or id == nil then
nickname = NICKNAME
id = ID
end
ONLINE[nickname] = id
writeOnlineList()
end
--
function takeOffline(nickname, id)
if nickname == nil or id == nil then
nickname = NICKNAME
id = ID
end
ONLINE[nickname] = nil
writeOnlineList()
end
--
function executeRemote(request)
local command
if request.message:sub(0, 1) == "/" then
command = request.message:sub(2)
if command == "online" then
putOnline(request.nickname, request.sender)
appendBuffer("[" .. OPERATOR .. "]: " .. request.nickname .. " is now online")
send("/metoo")
elseif command == "offline" then
takeOffline(request.nickname, request.sender)
appendBuffer("[" .. OPERATOR .. "]: " .. request.nickname .. " is now offline")
elseif command == "metoo" then
putOnline(request.nickname, request.sender)
end
return
end
if request.target ~= nil then
appendBuffer(request.nickname .. " > " .. NICKNAME .. ": " .. request.message)
LAST_MSG_TARGET = request.nickname
else
appendBuffer(request.nickname .. ": " .. request.message)
end
end
--
function appendBuffer(message)
local length
length = message:len()
if length > WIDTH then
table.insert(BUFFER, message:sub(1, WIDTH))
POINTER = POINTER + 1
appendBuffer(message:sub(WIDTH + 1))
else
table.insert(BUFFER, message)
POINTER = POINTER + 1
end
SCROLL_POINTER = POINTER
showBuffer()
end
--
function showBuffer()
local i, line, bufferPointer, x, y, pointer
pointer = SCROLL_POINTER
if pointer == 0 then
return
elseif SCROLL_POINTER > POINTER then
SCROLL_POINTER = POINTER
pointer = POINTER
elseif POINTER < LINES + 1 then
SCROLL_POINTER = POINTER
pointer = POINTER
elseif POINTER > LINES and SCROLL_POINTER < LINES then
SCROLL_POINTER = LINES
pointer = SCROLL_POINTER
end
x, y = term.getCursorPos()
line = START_LINE
bufferPointer = -(LINES - 1 - pointer)
for i = bufferPointer, bufferPointer + (LINES - 1) do
term.setCursorPos(1, line)
term.clearLine()
if BUFFER[i] ~= nil then
term.write(tostring(BUFFER[i]))
end
line = line + 1
end
term.setCursorPos(x, y)
end
-- Fire up the application
main()
No comments:
Post a Comment