-- GChat - chat program
-- by MysticT
-- Config
local nMinNameLen = 3 -- minimum user name lenght
local nMaxNameLen = 8 -- maximum user name lenght
local nMaxHistory = 50 -- maximum chat history size, set to nil to disable
-- System vars
local nScreenW, nScreenH = term.getSize()
local tUsers = {}
-- User message vars
local sUsrName = ""
local sUsrMsg = ""
local nPos = 0
-- Received messages vars
local tMessages = {}
local nMsg = 1
-- Menu vars
local bMenu = false
local nSelected = 1
local tMenuFunctions = {}
local tMenuOptions = {}
-- Menu functions vars
-- Add any variable you need here
local bExit = false
local function Clear()
term.clear()
term.setCursorPos(1, 1)
end
local function OpenAll()
for _,side in ipairs(rs.getSides()) do
rednet.open(side)
end
end
local function AddMessage(sMsg, sName)
if #sMsg > 0 then
local msg
if sName then
msg = sName..": "..sMsg
else
msg = "<"..sMsg..">"
end
table.insert(tMessages, msg)
if #tMessages - nMsg >= nScreenH - 1 then
nMsg = nMsg + 1
end
if nMaxHistory ~= nil and #tMessages > nMaxHistory then
table.remove(tMessages, 1)
if nMsg > 1 then
nMsg = nMsg - 1
end
end
end
end
local function AddUser(nID, sName)
tUsers[nID] = sName
AddMessage("User "..sName.." connected from "..tostring(nID))
end
local function RemoveUser(nID)
AddMessage("User "..tUsers[nID].." disconnected")
tUsers[nID] = nil
end
local function WriteMsg(sText)
local x, y = term.getCursorPos()
local function newLine()
x = 1
y = y + 1
term.setCursorPos(x, y)
if y < nScreenH - 2 then
return true
end
return false
end
while #sText > 0 do
local whitespace = string.match(sText, "^[ t]+")
if whitespace then
term.write(whitespace)
x, y = term.getCursorPos()
sText = string.sub(sText, #whitespace + 1)
end
local newline = string.match(sText, "^n")
if newline then
if not newLine() then
return true
end
sText = string.sub(sText, 2)
end
local text = string.match(sText, "^[^ tn]+")
if text then
sText = string.sub(sText, #text + 1)
if #text > nScreenW then
while #text > 0 do
if x > nScreenW then
if not newLine() then
return true
end
end
term.write(text)
text = string.sub(text, (nScreenW - x) + 2)
x, y = term.getCursorPos()
end
else
if x + #text > nScreenW then
if not newLine() then
return true
end
end
term.write(text)
x, y = term.getCursorPos()
end
end
end
return false
end
local function WriteMessages()
local i = 0
while nMsg + i <= #tMessages and i < nScreenH - 1 do
if WriteMsg(tMessages[nMsg + i]) then
break
end
local x, y = term.getCursorPos()
term.setCursorPos(1, y + 1)
i = i + 1
end
end
local function RedrawUserMsg()
local nScroll = 0
if nPos + 3 >= nScreenW then
nScroll = nPos + 3 - nScreenW
end
term.setCursorPos(1, nScreenH)
write("> ")
write(string.sub(sUsrMsg, nScroll + 1))
term.setCursorPos(nPos + 3 - nScroll, nScreenH)
end
local function RedrawMenu()
term.setCursorPos(1, nScreenH)
for i, s in ipairs(tMenuOptions) do
if i == nSelected then
term.write("["..s.."]")
else
term.write(s)
end
term.write(" ")
end
end
local function Redraw()
Clear()
WriteMessages()
if bMenu then
RedrawMenu()
else
RedrawUserMsg()
end
end
local function ParseMsg(nID, sMsg)
local sAction, sArgs = string.match(sMsg, "(%a+): (.+)")
if sAction == "MSG" then
AddMessage(sArgs, tUsers[nID])
elseif sAction == "PONG" then
AddUser(nID, sArgs)
elseif sAction == "PING" then
AddUser(nID, sArgs)
rednet.send(nID, "PONG: "..sUsrName)
elseif sAction == "QUIT" then
RemoveUser(nID)
end
end
local function Ping()
rednet.broadcast("PING: "..sUsrName)
end
local function Disconnect()
for id,_ in pairs(tUsers) do
rednet.send(id, "QUIT: "..sUsrName)
end
end
local function SendMsg()
AddMessage(sUsrMsg, sUsrName)
for id,_ in pairs(tUsers) do
rednet.send(id, "MSG: "..sUsrMsg)
end
sUsrMsg = ""
nPos = 0
end
local function DoMenuItem()
tMenuFunctions[nSelected]()
nSelected = 1
end
function KeyPress(key)
if key == 28 then -- Enter
if bMenu then
DoMenuItem()
else
SendMsg()
end
elseif key == 203 then -- Left
if bMenu then
if nSelected > 1 then
nSelected = nSelected - 1
end
else
if nPos > 0 then
nPos = nPos - 1
end
end
elseif key == 205 then -- Right
if bMenu then
if nSelected < #tMenuOptions then
nSelected = nSelected + 1
end
else
if nPos < #sUsrMsg then
nPos = nPos + 1
end
end
elseif key == 14 then -- Backspace
if not bMenu then
if nPos > 0 then
sUsrMsg = string.sub(sUsrMsg, 1, nPos - 1)..string.sub(sUsrMsg, nPos + 1)
nPos = nPos - 1
end
end
elseif key == 211 then -- Delete
if not bMenu then
sUsrMsg = string.sub(sUsrMsg, 1, nPos)..string.sub(sUsrMsg, nPos + 2)
end
elseif key == 199 then -- Start
if bMenu then
nSelected = 1
else
nPos = 0
end
elseif key == 207 then -- End
if bMenu then
nSelected = #tMenuOptions
else
nPos = #sUsrMsg
end
elseif key == 200 then -- Up
if nMsg > 1 then
nMsg = nMsg - 1
end
elseif key == 208 then -- Down
if nMsg < #tMessages then
nMsg = nMsg + 1
end
elseif key == 29 then -- Ctrl
bMenu = not bMenu
term.setCursorBlink(not bMenu)
end
end
local function AddChar(sChar)
sUsrMsg = string.sub(sUsrMsg, 1, nPos)..sChar..string.sub(sUsrMsg, nPos + 1)
nPos = nPos + 1
end
local function CheckUserName()
if #sUsrName < nMinNameLen then
return false, "Name too short."
elseif #sUsrName > nMaxNameLen then
return false, "Name too long."
end
return true
end
-- Menu
-- Add Menu functions here --
--[[
tMenuOptions[n] = "your option name"
tMenuFunctions[n] = function()
-- Your code here
end
--]]
table.insert(tMenuOptions, "Exit")
table.insert(tMenuFunctions, function()
bExit = true
end )
-- Main loop
repeat
Clear()
print("- Welcome to GChat -")
write("Enter your name: ")
sUsrName = read()
local bValid, err = CheckUserName()
if not bValid then
print(err)
sleep(2)
end
until bValid
OpenAll()
Ping()
term.setCursorBlink(true)
while not bExit do
Redraw()
local evt, arg1, arg2 = os.pullEvent()
if evt == "key" then
KeyPress(arg1)
elseif evt == "char" then
AddChar(arg1)
elseif evt == "rednet_message" then
ParseMsg(arg1, arg2)
end
end
Disconnect()
Clear()